From e43b737f2c1591fa03bbf126b4019645e0e13ac7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 08 2020 16:57:24 +0000 Subject: [PATCH 1/2232] Release 0.1.0 --- diff --git a/Cargo.toml b/Cargo.toml index a6d34cc..d111367 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "cxx" -version = "0.0.0" +version = "0.1.0" authors = ["David Tolnay "] edition = "2018" -links = "cxxbridge00" +links = "cxxbridge01" license = "MIT OR Apache-2.0" description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" @@ -18,7 +18,7 @@ anyhow = "1.0" cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" -cxxbridge-macro = { version = "0.0", path = "macro" } +cxxbridge-macro = { version = "0.1", path = "macro" } proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/README.md b/README.md index 5ea03ee..a978e96 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ using bindgen or cbindgen to generate unsafe C-style bindings. ```toml [dependencies] -cxx = "0.0" +cxx = "0.1" ``` *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March @@ -294,9 +294,9 @@ of functions. name in Rustname in C++restrictions Stringcxxbridge::RustString &strcxxbridge::RustStr -CxxStringstd::stringcannot be passed by value +CxxStringstd::stringcannot be passed by value Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type -UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type diff --git a/build.rs b/build.rs index 4d8292e..f16f265 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,7 @@ fn main() { cc::Build::new() .file("src/cxxbridge.cc") .flag("-std=c++11") - .compile("cxxbridge00"); + .compile("cxxbridge01"); println!("cargo:rustc-flags=-l dylib=stdc++"); println!("cargo:rerun-if-changed=src/cxxbridge.cc"); println!("cargo:rerun-if-changed=include/cxxbridge.h"); diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index b3f7719..0cfe883 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.0.0" +version = "0.1.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/write.rs b/gen/write.rs index fd92d50..81f26b8 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -124,10 +124,10 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } - out.begin_block("namespace cxxbridge00"); + out.begin_block("namespace cxxbridge01"); if needs_rust_box { writeln!(out, "// #include \"cxxbridge.h\""); - for line in include::get("CXXBRIDGE00_RUST_BOX").lines() { + for line in include::get("CXXBRIDGE01_RUST_BOX").lines() { if !line.trim_start().starts_with("//") { writeln!(out, "{}", line); } @@ -146,7 +146,7 @@ fn write_namespace_alias(out: &mut OutFile, types: &Types) { } if needs_namespace_alias { - writeln!(out, "namespace cxxbridge = cxxbridge00;"); + writeln!(out, "namespace cxxbridge = cxxbridge01;"); } } @@ -176,7 +176,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge00${}(", efn.ident); + write!(out, "cxxbridge01${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -244,7 +244,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge00${}(", efn.ident); + write!(out, "cxxbridge01${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -299,7 +299,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge00${}(", efn.ident); + write!(out, "cxxbridge01${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -435,7 +435,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } out.end_block(); - out.begin_block("namespace cxxbridge00"); + out.begin_block("namespace cxxbridge01"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -455,34 +455,34 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE00_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE00_RUST_BOX_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE01_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge00$rust_box${}$uninit(cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$uninit(cxxbridge::RustBox<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge00$rust_box${}$set_raw(cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$rust_box${}$set_raw(cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge00$rust_box${}$drop(cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$drop(cxxbridge::RustBox<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge00$rust_box${}$deref(const cxxbridge::RustBox<{}> *ptr) noexcept;", + "const {} *cxxbridge01$rust_box${}$deref(const cxxbridge::RustBox<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge00$rust_box${}$deref_mut(cxxbridge::RustBox<{}> *ptr) noexcept;", + "{} *cxxbridge01$rust_box${}$deref_mut(cxxbridge::RustBox<{}> *ptr) noexcept;", inner, instance, inner, ); - writeln!(out, "#endif // CXXBRIDGE00_RUST_BOX_{}", instance); + writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); } fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { @@ -498,7 +498,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "void RustBox<{}>::uninit() noexcept {{", inner); writeln!( out, - " return cxxbridge00$rust_box${}$uninit(this);", + " return cxxbridge01$rust_box${}$uninit(this);", instance ); writeln!(out, "}}"); @@ -511,7 +511,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - " return cxxbridge00$rust_box${}$set_raw(this, raw);", + " return cxxbridge01$rust_box${}$set_raw(this, raw);", instance ); writeln!(out, "}}"); @@ -520,7 +520,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "void RustBox<{}>::drop() noexcept {{", inner); writeln!( out, - " return cxxbridge00$rust_box${}$drop(this);", + " return cxxbridge01$rust_box${}$drop(this);", instance ); writeln!(out, "}}"); @@ -533,7 +533,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - " return cxxbridge00$rust_box${}$deref(this);", + " return cxxbridge01$rust_box${}$deref(this);", instance ); writeln!(out, "}}"); @@ -546,7 +546,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - " return cxxbridge00$rust_box${}$deref_mut(this);", + " return cxxbridge01$rust_box${}$deref_mut(this);", instance ); writeln!(out, "}}"); @@ -561,8 +561,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE00_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE00_UNIQUE_PTR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE01_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE01_UNIQUE_PTR_{}", instance); writeln!( out, "static_assert(sizeof(std::unique_ptr<{}>) == sizeof(void *), \"\");", @@ -575,14 +575,14 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - "void cxxbridge00$unique_ptr${}$null(std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge01$unique_ptr${}$null(std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " new (ptr) std::unique_ptr<{}>();", inner); writeln!(out, "}}"); writeln!( out, - "void cxxbridge00$unique_ptr${}$new(std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge01$unique_ptr${}$new(std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( @@ -593,31 +593,31 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!( out, - "void cxxbridge00$unique_ptr${}$raw(std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge01$unique_ptr${}$raw(std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); writeln!(out, " new (ptr) std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge00$unique_ptr${}$get(const std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge01$unique_ptr${}$get(const std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge00$unique_ptr${}$release(std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge01$unique_ptr${}$release(std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge00$unique_ptr${}$drop(std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge01$unique_ptr${}$drop(std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE00_UNIQUE_PTR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE01_UNIQUE_PTR_{}", instance); } diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 3135092..a0d9dbb 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -4,7 +4,7 @@ #include #include -namespace cxxbridge00 { +namespace cxxbridge01 { class RustString final { public: @@ -58,8 +58,8 @@ private: Repr repr; }; -#ifndef CXXBRIDGE00_RUST_BOX -#define CXXBRIDGE00_RUST_BOX +#ifndef CXXBRIDGE01_RUST_BOX +#define CXXBRIDGE01_RUST_BOX template class RustBox final { public: RustBox(const RustBox &other) : RustBox(*other) {} @@ -122,11 +122,11 @@ private: T *deref_mut() noexcept; uintptr_t repr; }; -#endif // CXXBRIDGE00_RUST_BOX +#endif // CXXBRIDGE01_RUST_BOX std::ostream &operator<<(std::ostream &os, const RustString &s); std::ostream &operator<<(std::ostream &os, const RustStr &s); -} // namespace cxxbridge00 +} // namespace cxxbridge01 -namespace cxxbridge = cxxbridge00; +namespace cxxbridge = cxxbridge01; diff --git a/macro/Cargo.toml b/macro/Cargo.toml index f7256e0..2d14527 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.0.0" +version = "0.1.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -19,4 +19,4 @@ quote = "1.0" syn = { version = "1.0", features = ["full"] } [dev-dependencies] -cxx = { version = "0.0", path = ".." } +cxx = { version = "0.1", path = ".." } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 63f7032..e631e67 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -128,7 +128,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = format!("{}cxxbridge00${}", namespace, ident); + let link_name = format!("{}cxxbridge01${}", namespace, ident); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] @@ -266,7 +266,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type expr = quote!(::std::ptr::write(__return, #expr)); } let ret = expand_extern_return_type(&efn.ret, types); - let link_name = format!("{}cxxbridge00${}", namespace, ident); + let link_name = format!("{}cxxbridge01${}", namespace, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); quote! { @@ -280,7 +280,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge00$rust_box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge01$rust_box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_set_raw = format!("{}set_raw", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -337,7 +337,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { - let prefix = format!("cxxbridge00$unique_ptr${}{}$", namespace, ident); + let prefix = format!("cxxbridge01$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); diff --git a/src/cxx_string.rs b/src/cxx_string.rs index c169797..40a1731 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -4,9 +4,9 @@ use std::slice; use std::str::{self, Utf8Error}; extern "C" { - #[link_name = "cxxbridge00$cxx_string$data"] + #[link_name = "cxxbridge01$cxx_string$data"] fn string_data(_: &CxxString) -> *const u8; - #[link_name = "cxxbridge00$cxx_string$length"] + #[link_name = "cxxbridge01$cxx_string$length"] fn string_length(_: &CxxString) -> usize; } diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 3d1a902..a502d20 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -3,48 +3,48 @@ #include #include -namespace cxxbridge = cxxbridge00; +namespace cxxbridge = cxxbridge01; extern "C" { -const char *cxxbridge00$cxx_string$data(const std::string &s) noexcept { +const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { return s.data(); } -size_t cxxbridge00$cxx_string$length(const std::string &s) noexcept { +size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { return s.length(); } // RustString -void cxxbridge00$rust_string$new(cxxbridge::RustString *self) noexcept; -void cxxbridge00$rust_string$clone(cxxbridge::RustString *self, +void cxxbridge01$rust_string$new(cxxbridge::RustString *self) noexcept; +void cxxbridge01$rust_string$clone(cxxbridge::RustString *self, const cxxbridge::RustString &other) noexcept; -bool cxxbridge00$rust_string$from(cxxbridge::RustString *self, const char *ptr, +bool cxxbridge01$rust_string$from(cxxbridge::RustString *self, const char *ptr, size_t len) noexcept; -void cxxbridge00$rust_string$drop(cxxbridge::RustString *self) noexcept; +void cxxbridge01$rust_string$drop(cxxbridge::RustString *self) noexcept; const char * -cxxbridge00$rust_string$ptr(const cxxbridge::RustString *self) noexcept; -size_t cxxbridge00$rust_string$len(const cxxbridge::RustString *self) noexcept; +cxxbridge01$rust_string$ptr(const cxxbridge::RustString *self) noexcept; +size_t cxxbridge01$rust_string$len(const cxxbridge::RustString *self) noexcept; // RustStr -bool cxxbridge00$rust_str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; } // extern "C" -namespace cxxbridge00 { +namespace cxxbridge01 { -RustString::RustString() noexcept { cxxbridge00$rust_string$new(this); } +RustString::RustString() noexcept { cxxbridge01$rust_string$new(this); } RustString::RustString(const RustString &other) noexcept { - cxxbridge00$rust_string$clone(this, other); + cxxbridge01$rust_string$clone(this, other); } RustString::RustString(RustString &&other) noexcept { this->repr = other.repr; - cxxbridge00$rust_string$new(&other); + cxxbridge01$rust_string$new(&other); } RustString::RustString(const char *s) { auto len = strlen(s); - if (!cxxbridge00$rust_string$from(this, s, len)) { + if (!cxxbridge01$rust_string$from(this, s, len)) { throw std::invalid_argument("data for RustString is not utf-8"); } } @@ -52,12 +52,12 @@ RustString::RustString(const char *s) { RustString::RustString(const std::string &s) { auto ptr = s.data(); auto len = s.length(); - if (!cxxbridge00$rust_string$from(this, ptr, len)) { + if (!cxxbridge01$rust_string$from(this, ptr, len)) { throw std::invalid_argument("data for RustString is not utf-8"); } } -RustString::~RustString() noexcept { cxxbridge00$rust_string$drop(this); } +RustString::~RustString() noexcept { cxxbridge01$rust_string$drop(this); } RustString::operator std::string() const { return std::string(this->data(), this->size()); @@ -65,31 +65,31 @@ RustString::operator std::string() const { RustString &RustString::operator=(const RustString &other) noexcept { if (this != &other) { - cxxbridge00$rust_string$drop(this); - cxxbridge00$rust_string$clone(this, other); + cxxbridge01$rust_string$drop(this); + cxxbridge01$rust_string$clone(this, other); } return *this; } RustString &RustString::operator=(RustString &&other) noexcept { if (this != &other) { - cxxbridge00$rust_string$drop(this); + cxxbridge01$rust_string$drop(this); this->repr = other.repr; - cxxbridge00$rust_string$new(&other); + cxxbridge01$rust_string$new(&other); } return *this; } const char *RustString::data() const noexcept { - return cxxbridge00$rust_string$ptr(this); + return cxxbridge01$rust_string$ptr(this); } size_t RustString::size() const noexcept { - return cxxbridge00$rust_string$len(this); + return cxxbridge01$rust_string$len(this); } size_t RustString::length() const noexcept { - return cxxbridge00$rust_string$len(this); + return cxxbridge01$rust_string$len(this); } std::ostream &operator<<(std::ostream &os, const RustString &s) { @@ -101,13 +101,13 @@ RustStr::RustStr() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} RustStr::RustStr(const char *s) : repr(Repr{s, strlen(s)}) { - if (!cxxbridge00$rust_str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for RustStr is not utf-8"); } } RustStr::RustStr(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge00$rust_str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for RustStr is not utf-8"); } } @@ -138,30 +138,30 @@ std::ostream &operator<<(std::ostream &os, const RustStr &s) { return os; } -} // namespace cxxbridge00 +} // namespace cxxbridge01 extern "C" { -void cxxbridge00$unique_ptr$std$string$null( +void cxxbridge01$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge00$unique_ptr$std$string$new(std::unique_ptr *ptr, +void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, std::string *value) noexcept { new (ptr) std::unique_ptr(new std::string(std::move(*value))); } -void cxxbridge00$unique_ptr$std$string$raw(std::unique_ptr *ptr, +void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); } -const std::string *cxxbridge00$unique_ptr$std$string$get( +const std::string *cxxbridge01$unique_ptr$std$string$get( const std::unique_ptr &ptr) noexcept { return ptr.get(); } -std::string *cxxbridge00$unique_ptr$std$string$release( +std::string *cxxbridge01$unique_ptr$std$string$release( std::unique_ptr &ptr) noexcept { return ptr.release(); } -void cxxbridge00$unique_ptr$std$string$drop( +void cxxbridge01$unique_ptr$std$string$drop( std::unique_ptr *ptr) noexcept { ptr->~unique_ptr(); } diff --git a/src/lib.rs b/src/lib.rs index 8ed9572..5c018c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -298,9 +298,9 @@ //! name in Rustname in C++restrictions //! Stringcxxbridge::RustString //! &strcxxbridge::RustStr -//! CxxStringstd::stringcannot be passed by value +//! CxxStringstd::stringcannot be passed by value //! Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type -//! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +//! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type //! //! //! diff --git a/src/rust_str.rs b/src/rust_str.rs index 5aeac28..59a7784 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -23,7 +23,7 @@ impl RustStr { } } -#[export_name = "cxxbridge00$rust_str$valid"] +#[export_name = "cxxbridge01$rust_str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/rust_string.rs b/src/rust_string.rs index 5756efa..250a46f 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -26,17 +26,17 @@ impl RustString { } } -#[export_name = "cxxbridge00$rust_string$new"] +#[export_name = "cxxbridge01$rust_string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge00$rust_string$clone"] +#[export_name = "cxxbridge01$rust_string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge00$rust_string$from"] +#[export_name = "cxxbridge01$rust_string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -52,17 +52,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge00$rust_string$drop"] +#[export_name = "cxxbridge01$rust_string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge00$rust_string$ptr"] +#[export_name = "cxxbridge01$rust_string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge00$rust_string$len"] +#[export_name = "cxxbridge01$rust_string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index f6d2e86..19718bb 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -130,17 +130,17 @@ pub unsafe trait UniquePtrTarget { } extern "C" { - #[link_name = "cxxbridge00$unique_ptr$std$string$null"] + #[link_name = "cxxbridge01$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge00$unique_ptr$std$string$new"] + #[link_name = "cxxbridge01$unique_ptr$std$string$new"] fn unique_ptr_std_string_new(this: *mut *mut c_void, value: *mut CxxString); - #[link_name = "cxxbridge00$unique_ptr$std$string$raw"] + #[link_name = "cxxbridge01$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); - #[link_name = "cxxbridge00$unique_ptr$std$string$get"] + #[link_name = "cxxbridge01$unique_ptr$std$string$get"] fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; - #[link_name = "cxxbridge00$unique_ptr$std$string$release"] + #[link_name = "cxxbridge01$unique_ptr$std$string$release"] fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; - #[link_name = "cxxbridge00$unique_ptr$std$string$drop"] + #[link_name = "cxxbridge01$unique_ptr$std$string$drop"] fn unique_ptr_std_string_drop(this: *mut *mut c_void); } From ccd3975f4086fc6dbc87e0b1c53b15122dd48c2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 08 2020 17:35:04 +0000 Subject: [PATCH 2/2232] Add a brief safety explanation up top --- diff --git a/README.md b/README.md index a978e96..cdd2e51 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ This library provides a **safe** mechanism for calling C++ code from Rust and Rust code from C++, not subject to the many ways that things can go wrong when using bindgen or cbindgen to generate unsafe C-style bindings. +This doesn't change the fact that 100% of C++ code is unsafe. When auditing a +project, you would be on the hook for auditing all the unsafe Rust code and +*all* the C++ code. The core safety claim under this new model is that auditing +just the C++ side would be sufficient to catch all problems, i.e. the Rust side +can be 100% safe. + ```toml [dependencies] cxx = "0.1" diff --git a/src/lib.rs b/src/lib.rs index 5c018c2..50bf516 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,12 @@ //! and Rust code from C++, not subject to the many ways that things can go //! wrong when using bindgen or cbindgen to generate unsafe C-style bindings. //! +//! This doesn't change the fact that 100% of C++ code is unsafe. When auditing +//! a project, you would be on the hook for auditing all the unsafe Rust code +//! and *all* the C++ code. The core safety claim under this new model is that +//! auditing just the C++ side would be sufficient to catch all problems, i.e. +//! the Rust side can be 100% safe. +//! //!
//! //! *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March From 84f232ed0b8e3d871d94dc4fbf71946dcd5d039c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 08 2020 20:22:56 +0000 Subject: [PATCH 3/2232] Fill in blank table cells with placeholder --- diff --git a/README.md b/README.md index cdd2e51..e7eaa3a 100644 --- a/README.md +++ b/README.md @@ -316,13 +316,13 @@ matter of designing a nice API for each in its non-native language. - - - - - - - + + + + + + +
name in Rustname in C++
&[T]
Vec<T>
BTreeMap<K, V>
HashMap<K, V>
std::vector<T>
std::map<K, V>
std::unordered_map<K, V>
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>

diff --git a/src/lib.rs b/src/lib.rs index 50bf516..5331084 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -320,13 +320,13 @@ //! //! //! -//! -//! -//! -//! -//! -//! -//! +//! +//! +//! +//! +//! +//! +//! //!
name in Rustname in C++
&[T]
Vec<T>
BTreeMap<K, V>
HashMap<K, V>
std::vector<T>
std::map<K, V>
std::unordered_map<K, V>
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
#![deny(improper_ctypes)] From 17955e2e8c73f132191db4ea213c5e73ebdb4c9b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:09:41 +0000 Subject: [PATCH 4/2232] Implement special case types in extern Rust argument position --- diff --git a/gen/write.rs b/gen/write.rs index 81f26b8..eb11470 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -308,6 +308,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "&"); } write!(out, "{}", arg.ident); + match arg.ty { + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::UniquePtr(_) => write!(out, ".release()"), + _ => {} + } } if indirect_return { if !efn.args.is_empty() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e631e67..ac10b78 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -231,10 +231,21 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type let args = efn.args.iter().map(|arg| expand_extern_arg(arg, types)); let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; - if types.needs_indirect_abi(&arg.ty) { + let var = if types.needs_indirect_abi(&arg.ty) { quote!(::std::ptr::read(#ident)) } else { quote!(#ident) + }; + match &arg.ty { + Type::Ident(ident) if ident == "String" => quote!(#var.into_string()), + Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#var)), + Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#var)), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == "String" => quote!(#var.as_string()), + _ => var, + }, + Type::Str(_) => quote!(#var.as_str()), + _ => var, } }); let mut outparam = None; From 94a5cdb033160f2dbb87cf7b9b41000ed48a97f8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:11:32 +0000 Subject: [PATCH 5/2232] Merge pull request #14 from dtolnay/rustarg Implement special case types in extern Rust argument position --- diff --git a/gen/write.rs b/gen/write.rs index 81f26b8..eb11470 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -308,6 +308,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "&"); } write!(out, "{}", arg.ident); + match arg.ty { + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::UniquePtr(_) => write!(out, ".release()"), + _ => {} + } } if indirect_return { if !efn.args.is_empty() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e631e67..ac10b78 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -231,10 +231,21 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type let args = efn.args.iter().map(|arg| expand_extern_arg(arg, types)); let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; - if types.needs_indirect_abi(&arg.ty) { + let var = if types.needs_indirect_abi(&arg.ty) { quote!(::std::ptr::read(#ident)) } else { quote!(#ident) + }; + match &arg.ty { + Type::Ident(ident) if ident == "String" => quote!(#var.into_string()), + Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#var)), + Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#var)), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == "String" => quote!(#var.as_string()), + _ => var, + }, + Type::Str(_) => quote!(#var.as_str()), + _ => var, } }); let mut outparam = None; From 159a9f535cca686ae795780a5daefe038eea5221 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:13:00 +0000 Subject: [PATCH 6/2232] Release 0.1.1 --- diff --git a/Cargo.toml b/Cargo.toml index d111367..4018cb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.1.0" +version = "0.1.1" authors = ["David Tolnay "] edition = "2018" links = "cxxbridge01" @@ -18,7 +18,7 @@ anyhow = "1.0" cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" -cxxbridge-macro = { version = "0.1", path = "macro" } +cxxbridge-macro = { version = "=0.1.1", path = "macro" } proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 0cfe883..1a0b4ba 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.1.0" +version = "0.1.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 2d14527..fc216ba 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.1.0" +version = "0.1.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 8861bee797c81aa501e6c30797e83dafcc19456e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:40:16 +0000 Subject: [PATCH 7/2232] Support opaque types that are not structs --- diff --git a/gen/write.rs b/gen/write.rs index eb11470..97fdc48 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -40,7 +40,8 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b for api in apis { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) | Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident), _ => {} } } @@ -167,6 +168,10 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } +fn write_struct_using(out: &mut OutFile, ident: &Ident) { + writeln!(out, "using {} = {};", ident, ident); +} + fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { let indirect_return = efn .ret From 199d73509b5c89c602e4fc58687689d54ea76541 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:40:16 +0000 Subject: [PATCH 8/2232] Format with rustfmt 2019-10-07 --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ac10b78..bcd02f1 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -23,7 +23,9 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); if !has_rust_type { - hidden.extend(quote!(const fn __assert_sized() {})); + hidden.extend(quote!( + const fn __assert_sized() {} + )); has_rust_type = true; } let ident = &ety.ident; From 270b8851327dc90846bd9f1f545e92c3a21fbb5c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:50:52 +0000 Subject: [PATCH 9/2232] Merge pull request #15 from dtolnay/opaque Support opaque types that are not structs --- diff --git a/gen/write.rs b/gen/write.rs index eb11470..97fdc48 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -40,7 +40,8 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b for api in apis { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) | Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident), _ => {} } } @@ -167,6 +168,10 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } +fn write_struct_using(out: &mut OutFile, ident: &Ident) { + writeln!(out, "using {} = {};", ident, ident); +} + fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { let indirect_return = efn .ret From 61b6771334bf0dda141d8acb130d3d2d7ebf67d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 21 2020 02:53:20 +0000 Subject: [PATCH 10/2232] Release 0.1.2 --- diff --git a/Cargo.toml b/Cargo.toml index 4018cb2..118c7ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.1.1" +version = "0.1.2" authors = ["David Tolnay "] edition = "2018" links = "cxxbridge01" @@ -18,7 +18,7 @@ anyhow = "1.0" cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" -cxxbridge-macro = { version = "=0.1.1", path = "macro" } +cxxbridge-macro = { version = "=0.1.2", path = "macro" } proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 1a0b4ba..f39609a 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.1.1" +version = "0.1.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index fc216ba..8159ec7 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.1.1" +version = "0.1.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From af60e239b054a653cf5c1fb27cc4f7451f18e0d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 24 2020 23:23:20 +0000 Subject: [PATCH 11/2232] Use platform's default C++ standard library --- diff --git a/Cargo.toml b/Cargo.toml index 118c7ad..b120173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" cxxbridge-macro = { version = "=0.1.2", path = "macro" } +link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/build.rs b/build.rs index f16f265..ea562c2 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,6 @@ fn main() { .file("src/cxxbridge.cc") .flag("-std=c++11") .compile("cxxbridge01"); - println!("cargo:rustc-flags=-l dylib=stdc++"); println!("cargo:rerun-if-changed=src/cxxbridge.cc"); println!("cargo:rerun-if-changed=include/cxxbridge.h"); } diff --git a/src/lib.rs b/src/lib.rs index 5331084..509cfa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -342,6 +342,8 @@ clippy::useless_let_if_seq )] +extern crate link_cplusplus; + mod cxx_string; mod error; mod gen; From 05e9cc4d9b9c86a367e3d46c65c7d2135cbba650 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 25 2020 00:07:20 +0000 Subject: [PATCH 12/2232] Merge pull request #21 from dtolnay/link Use platform's default C++ standard library --- diff --git a/Cargo.toml b/Cargo.toml index 118c7ad..b120173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" cxxbridge-macro = { version = "=0.1.2", path = "macro" } +link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/build.rs b/build.rs index f16f265..ea562c2 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,6 @@ fn main() { .file("src/cxxbridge.cc") .flag("-std=c++11") .compile("cxxbridge01"); - println!("cargo:rustc-flags=-l dylib=stdc++"); println!("cargo:rerun-if-changed=src/cxxbridge.cc"); println!("cargo:rerun-if-changed=include/cxxbridge.h"); } diff --git a/src/lib.rs b/src/lib.rs index 5331084..509cfa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -342,6 +342,8 @@ clippy::useless_let_if_seq )] +extern crate link_cplusplus; + mod cxx_string; mod error; mod gen; From 4a44122b0f49daefcca68572d5707b9d0f7f02c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 26 2020 00:26:54 +0000 Subject: [PATCH 13/2232] Resolve Wreturn-type-c-linkage warnings --- diff --git a/gen/write.rs b/gen/write.rs index 97fdc48..ea6a812 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -211,8 +211,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "new (return$) "); write_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); - } else if efn.ret.is_some() { + } else if let Some(ret) = &efn.ret { write!(out, "return "); + if let Type::Ref(_) = ret { + write!(out, "&"); + } } write!(out, "{}$(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { @@ -298,8 +301,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, efn.ret.as_ref().unwrap()); writeln!(out, ")];"); write!(out, " "); - } else if efn.ret.is_some() { + } else if let Some(ret) = &efn.ret { write!(out, "return "); + if let Type::Ref(_) = ret { + write!(out, "*"); + } } for name in out.namespace.clone() { write!(out, "{}$", name); @@ -352,6 +358,13 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type_space(out, &ty.inner); write!(out, "*"); } + Some(Type::Ref(ty)) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } Some(Type::Str(_)) => write!(out, "cxxbridge::RustStr::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), From 152119246e40e8b6366c1378e4de93d758ae203f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 26 2020 00:32:33 +0000 Subject: [PATCH 14/2232] Merge pull request #23 from dtolnay/retref Resolve Wreturn-type-c-linkage warnings --- diff --git a/gen/write.rs b/gen/write.rs index 97fdc48..ea6a812 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -211,8 +211,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "new (return$) "); write_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); - } else if efn.ret.is_some() { + } else if let Some(ret) = &efn.ret { write!(out, "return "); + if let Type::Ref(_) = ret { + write!(out, "&"); + } } write!(out, "{}$(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { @@ -298,8 +301,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, efn.ret.as_ref().unwrap()); writeln!(out, ")];"); write!(out, " "); - } else if efn.ret.is_some() { + } else if let Some(ret) = &efn.ret { write!(out, "return "); + if let Type::Ref(_) = ret { + write!(out, "*"); + } } for name in out.namespace.clone() { write!(out, "{}$", name); @@ -352,6 +358,13 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type_space(out, &ty.inner); write!(out, "*"); } + Some(Type::Ref(ty)) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } Some(Type::Str(_)) => write!(out, "cxxbridge::RustStr::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), From 366ef8b486c0c433355fc5d5da240e1066f65096 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 26 2020 22:15:59 +0000 Subject: [PATCH 15/2232] Print error cause chain on failures in build.rs --- diff --git a/src/lib.rs b/src/lib.rs index 509cfa6..3267b3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -370,6 +370,7 @@ pub mod private { } use crate::error::Result; +use anyhow::anyhow; use std::fs; use std::io::{self, Write}; use std::path::Path; @@ -442,7 +443,7 @@ impl Build { match try_generate_bridge(rust_source_file.as_ref()) { Ok(build) => build, Err(err) => { - let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", err); + let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); process::exit(1); } } From 015c5e85a543c0623ca76aad547ff48d6ba5cefc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 27 2020 00:31:48 +0000 Subject: [PATCH 16/2232] Remove strip_prefix failure mode from relative paths --- diff --git a/src/error.rs b/src/error.rs index a05f7c8..0c19f98 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,14 @@ use std::io; -use std::path::StripPrefixError; use thiserror::Error; pub(super) type Result = std::result::Result; #[derive(Error, Debug)] -#[error(transparent)] pub(super) enum Error { #[error("missing OUT_DIR environment variable")] MissingOutDir, #[error("failed to locate target dir")] TargetDir, + #[error(transparent)] Io(#[from] io::Error), - StripPrefix(#[from] StripPrefixError), } diff --git a/src/paths.rs b/src/paths.rs index 866d377..8e5d717 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -44,10 +44,17 @@ fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { fn relative_to_parent_of_target_dir(original: &Path) -> Result { let target_dir = target_dir()?; - let parent_of_target_dir = target_dir.parent().unwrap(); + let mut outer = target_dir.parent().unwrap(); let original = original.canonicalize()?; - let suffix = original.strip_prefix(parent_of_target_dir)?; - Ok(suffix.to_owned()) + loop { + if let Ok(suffix) = original.strip_prefix(outer) { + return Ok(suffix.to_owned()); + } + match outer.parent() { + Some(parent) => outer = parent, + None => return Ok(original.components().skip(1).collect()), + } + } } pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { From 0a2d117fbab513788f42fc110c4a624fe7357c02 Mon Sep 17 00:00:00 2001 From: RS Date: Jan 28 2020 05:47:37 +0000 Subject: [PATCH 17/2232] Emit cxxbridge (#27) * Working cxxbridge output. * Add a way to emit cxxbridge.h --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index daa5ac0..96d21a5 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -1,7 +1,8 @@ mod gen; mod syntax; -use std::io::{self, Write}; +use gen::include::get_full_cxxbridge; +use std::io::{self, Error, ErrorKind, Write}; use std::path::PathBuf; use structopt::StructOpt; @@ -10,20 +11,28 @@ use structopt::StructOpt; struct Opt { /// Input Rust source file containing #[cxx::bridge] #[structopt(parse(from_os_str))] - input: PathBuf, + input: Option, - /// Emit header with declarations only + /// Emit header with declarations only. If no input is specified, emit cxxbridge.h #[structopt(long)] header: bool, } -fn main() { +fn main() -> Result<(), Error> { let opt = Opt::from_args(); - let gen = if opt.header { - gen::do_generate_header + + if let Some(input) = opt.input { + let gen = if opt.header { + gen::do_generate_header + } else { + gen::do_generate_bridge + }; + let bridge = gen(&input); + io::stdout().lock().write_all(bridge.as_ref()) + } else if opt.header { + io::stdout().lock().write_all(get_full_cxxbridge().as_ref()) } else { - gen::do_generate_bridge - }; - let bridge = gen(&opt.input); - let _ = io::stdout().lock().write_all(bridge.as_ref()); + let mut clap = Opt::clap().after_help(""); + clap.print_help().or(Err(Error::new(ErrorKind::Other, "Failed to write help"))) + } } diff --git a/gen/include.rs b/gen/include.rs index 00e67f0..f38ae73 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -11,3 +11,7 @@ pub fn get(guard: &str) -> &'static str { panic!("not found in cxxbridge.h header: {}", guard) } } + +pub fn get_full_cxxbridge() -> &'static str { + return HEADER +} From 7eb9c6b2a09932d0a6285382e1709ce70faa860a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 28 2020 06:35:59 +0000 Subject: [PATCH 18/2232] Touch up cxxbridge.h emit PR --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 96d21a5..8550261 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -1,9 +1,10 @@ mod gen; mod syntax; -use gen::include::get_full_cxxbridge; -use std::io::{self, Error, ErrorKind, Write}; +use gen::include; +use std::io::{self, Write}; use std::path::PathBuf; +use std::process; use structopt::StructOpt; #[derive(StructOpt, Debug)] @@ -13,12 +14,12 @@ struct Opt { #[structopt(parse(from_os_str))] input: Option, - /// Emit header with declarations only. If no input is specified, emit cxxbridge.h + /// Emit header with declarations only #[structopt(long)] header: bool, } -fn main() -> Result<(), Error> { +fn main() { let opt = Opt::from_args(); if let Some(input) = opt.input { @@ -28,11 +29,12 @@ fn main() -> Result<(), Error> { gen::do_generate_bridge }; let bridge = gen(&input); - io::stdout().lock().write_all(bridge.as_ref()) + let _ = io::stdout().lock().write_all(bridge.as_ref()); } else if opt.header { - io::stdout().lock().write_all(get_full_cxxbridge().as_ref()) + let header = include::HEADER; + let _ = io::stdout().lock().write_all(header.as_ref()); } else { - let mut clap = Opt::clap().after_help(""); - clap.print_help().or(Err(Error::new(ErrorKind::Other, "Failed to write help"))) + let _ = Opt::clap().after_help("").print_help(); + process::exit(1); } } diff --git a/gen/include.rs b/gen/include.rs index f38ae73..d4ff758 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -1,4 +1,4 @@ -static HEADER: &str = include_str!("include/cxxbridge.h"); +pub static HEADER: &str = include_str!("include/cxxbridge.h"); pub fn get(guard: &str) -> &'static str { let ifndef = format!("#ifndef {}\n", guard); @@ -11,7 +11,3 @@ pub fn get(guard: &str) -> &'static str { panic!("not found in cxxbridge.h header: {}", guard) } } - -pub fn get_full_cxxbridge() -> &'static str { - return HEADER -} From 4aea27578878bdd8416a2c95defcef57d9d65302 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 28 2020 06:55:55 +0000 Subject: [PATCH 19/2232] Use clap's required_unless to enforce input file path --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 8550261..c0b4cc4 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -4,14 +4,13 @@ mod syntax; use gen::include; use std::io::{self, Write}; use std::path::PathBuf; -use std::process; use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt(name = "cxxbridge", author)] struct Opt { /// Input Rust source file containing #[cxx::bridge] - #[structopt(parse(from_os_str))] + #[structopt(parse(from_os_str), required_unless = "header")] input: Option, /// Emit header with declarations only @@ -19,22 +18,17 @@ struct Opt { header: bool, } +fn write(content: impl AsRef<[u8]>) { + let _ = io::stdout().lock().write_all(content.as_ref()); +} + fn main() { let opt = Opt::from_args(); - if let Some(input) = opt.input { - let gen = if opt.header { - gen::do_generate_header - } else { - gen::do_generate_bridge - }; - let bridge = gen(&input); - let _ = io::stdout().lock().write_all(bridge.as_ref()); - } else if opt.header { - let header = include::HEADER; - let _ = io::stdout().lock().write_all(header.as_ref()); - } else { - let _ = Opt::clap().after_help("").print_help(); - process::exit(1); + match (opt.input, opt.header) { + (Some(input), true) => write(gen::do_generate_header(&input)), + (Some(input), false) => write(gen::do_generate_bridge(&input)), + (None, true) => write(include::HEADER), + (None, false) => unreachable!(), // enforced by required_unless } } From fa66e2afa6f43034a564ded0f4ee8a6555f7160d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 28 2020 07:24:40 +0000 Subject: [PATCH 20/2232] Customize usage message of cmd --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index c0b4cc4..26b6ef6 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -7,7 +7,17 @@ use std::path::PathBuf; use structopt::StructOpt; #[derive(StructOpt, Debug)] -#[structopt(name = "cxxbridge", author)] +#[structopt( + name = "cxxbridge", + author, + about = "https://github.com/dtolnay/cxx", + usage = "\ + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit cxxbridge.h header to stdout", + help_message = "Print help information", + version_message = "Print version information" +)] struct Opt { /// Input Rust source file containing #[cxx::bridge] #[structopt(parse(from_os_str), required_unless = "header")] From 0c88e03a4ae2eee4b6c2585bab6f7986b925f02d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 28 2020 08:43:51 +0000 Subject: [PATCH 21/2232] Remove any existing link before writing symlink --- diff --git a/src/paths.rs b/src/paths.rs index 8e5d717..16bf12a 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -31,9 +31,10 @@ pub(crate) fn symlink_header(path: &Path, original: &Path) { fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { let suffix = relative_to_parent_of_target_dir(original)?; - let dst = target_dir()?.join("cxxbridge").join(suffix); + let ref dst = target_dir()?.join("cxxbridge").join(suffix); fs::create_dir_all(dst.parent().unwrap())?; + let _ = fs::remove_file(dst); #[cfg(unix)] os::unix::fs::symlink(path, dst)?; #[cfg(windows)] From c43627aee6c9caf021e6a931687aea631579f37b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jan 28 2020 08:50:25 +0000 Subject: [PATCH 22/2232] Always write cxxbridge.h into a predictable place from build script --- diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 51dd81b..417edfb 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -1,5 +1,5 @@ #pragma once -#include "../include/cxxbridge.h" +#include "cxxbridge/cxxbridge.h" #include #include diff --git a/src/lib.rs b/src/lib.rs index 3267b3f..6f40124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -463,5 +463,10 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let mut build = paths::cc_build(); build.file(&bridge_path); + let ref cxxbridge_h = paths::include_dir()?.join("cxxbridge/cxxbridge.h"); + let _ = fs::create_dir_all(cxxbridge_h.parent().unwrap()); + let _ = fs::remove_file(cxxbridge_h); + let _ = fs::write(cxxbridge_h, gen::include::HEADER); + Ok(build) } diff --git a/src/paths.rs b/src/paths.rs index 16bf12a..f253939 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -15,11 +15,9 @@ pub(crate) fn cc_build() -> cc::Build { } fn try_cc_build() -> Result { - let target_dir = target_dir()?; - let mut build = cc::Build::new(); - build.include(target_dir.join("cxxbridge")); - build.include(target_dir.parent().unwrap()); + build.include(include_dir()?); + build.include(target_dir()?.parent().unwrap()); Ok(build) } @@ -31,7 +29,7 @@ pub(crate) fn symlink_header(path: &Path, original: &Path) { fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { let suffix = relative_to_parent_of_target_dir(original)?; - let ref dst = target_dir()?.join("cxxbridge").join(suffix); + let ref dst = include_dir()?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); @@ -67,6 +65,11 @@ pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { Ok(out_dir.join(rel).with_file_name(file_name)) } +pub(crate) fn include_dir() -> Result { + let target_dir = target_dir()?; + Ok(target_dir.join("cxxbridge")) +} + fn target_dir() -> Result { let mut dir = out_dir()?.canonicalize()?; loop { From f4b2421805be92610fc4c5e964953e9e983453ee Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Feb 03 2020 18:10:13 +0000 Subject: [PATCH 23/2232] Add macos and cargo test --- diff --git a/.travis.yml b/.travis.yml index af5dce8..1933ec6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ language: rust rust: nightly -script: cargo run --manifest-path demo-rs/Cargo.toml +os: + - linux + - macos +script: cargo run --manifest-path demo-rs/Cargo.toml && cargo test From e45bd7c842c7351eec27c791eef7316f6580ba95 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 03 2020 18:35:41 +0000 Subject: [PATCH 24/2232] Merge pull request #31 from sayrer/travis_macos Add macos and cargo test --- diff --git a/.travis.yml b/.travis.yml index af5dce8..1933ec6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ language: rust rust: nightly -script: cargo run --manifest-path demo-rs/Cargo.toml +os: + - linux + - macos +script: cargo run --manifest-path demo-rs/Cargo.toml && cargo test From f184feb74776e4ef39ffd4b75ade51d27a678090 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 03 2020 18:37:36 +0000 Subject: [PATCH 25/2232] Space out the Travis config --- diff --git a/.travis.yml b/.travis.yml index 1933ec6..f054344 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,11 @@ language: rust + rust: nightly + os: - linux - macos -script: cargo run --manifest-path demo-rs/Cargo.toml && cargo test + +script: + - cargo run --manifest-path demo-rs/Cargo.toml + - cargo test From bd3a6b27fbe857c1861ab645c9c0386f6173e421 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 08 2020 00:58:20 +0000 Subject: [PATCH 26/2232] Update ui tests to nightly-2020-02-08 --- diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 419b83d..1146b80 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -10,3 +10,4 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` = note: to learn more, visit = note: required because it appears within the type `TypeR` + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From 5bde59dd2de886b7bfc5989acb7a2227bd21bf4a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 18 2020 09:29:40 +0000 Subject: [PATCH 27/2232] Add buck targets --- diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 0000000..33c15ec --- /dev/null +++ b/.buckconfig @@ -0,0 +1,12 @@ +[project] + # We use some symlinks in the source tree, but they get eliminated by `cargo + # publish` and `cargo vendor` so this allow_symlinks setting should not be + # required downstream. + allow_symlinks = allow + +[cxx] + cxxflags = -std=c++11 + +[rust] + default_edition = 2018 + rustc_flags = -Crelocation-model=dynamic-no-pic --cap-lints=allow diff --git a/.gitignore b/.gitignore index a874449..b403b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/.buckd +/buck-out /Cargo.lock /expand.cc /expand.rs diff --git a/.travis.yml b/.travis.yml index f054344..74f438d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,3 +9,18 @@ os: script: - cargo run --manifest-path demo-rs/Cargo.toml - cargo test + +matrix: + include: + - name: Buck + before_install: + - sudo apt-get install -y openjdk-8-jdk + - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + - wget -O buck.deb https://github.com/facebook/buck/releases/download/v2019.10.17.01/buck.2019.10.17.01_all.deb + - sudo dpkg -i buck.deb + before_script: + - cp third-party/Cargo.lock . + - cargo vendor --versioned-dirs third-party/vendor + script: + - buck build :cxx#check --verbose=0 + - buck run demo-rs --verbose=0 diff --git a/BUCK b/BUCK new file mode 100644 index 0000000..2d12753 --- /dev/null +++ b/BUCK @@ -0,0 +1,60 @@ +rust_library( + name = "cxx", + srcs = glob(["src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":core", + ":macro", + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan", + "//third-party:codespan-reporting", + "//third-party:link-cplusplus", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +rust_binary( + name = "codegen", + srcs = glob(["cmd/src/**"]), + visibility = ["PUBLIC"], + env = { + "CARGO_PKG_AUTHORS": "David Tolnay ", + }, + deps = [ + "//third-party:anyhow", + "//third-party:codespan", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:structopt", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +cxx_library( + name = "core", + srcs = ["src/cxxbridge.cc"], + visibility = ["PUBLIC"], + header_namespace = "cxxbridge", + exported_headers = { + "cxxbridge.h": "include/cxxbridge.h", + }, + exported_linker_flags = ["-lstdc++"], +) + +rust_library( + name = "macro", + srcs = glob(["macro/src/**"]), + proc_macro = True, + crate = "cxxbridge_macro", + deps = [ + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK new file mode 100644 index 0000000..595a18b --- /dev/null +++ b/demo-cxx/BUCK @@ -0,0 +1,16 @@ +cxx_library( + name = "demo-cxx", + srcs = ["demo.cc"], + visibility = ["PUBLIC"], + deps = [ + ":include", + "//demo-rs:include", + ], +) + +cxx_library( + name = "include", + exported_headers = ["demo.h"], + visibility = ["PUBLIC"], + deps = ["//:core"], +) diff --git a/demo-rs/BUCK b/demo-rs/BUCK new file mode 100644 index 0000000..a0f6fb0 --- /dev/null +++ b/demo-rs/BUCK @@ -0,0 +1,42 @@ +rust_binary( + name = "demo-rs", + srcs = glob(["src/**"]), + deps = [ + ":gen", + "//:cxx", + "//demo-cxx:demo-cxx", + ], +) + +cxx_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":include", + "//demo-cxx:include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", + out = "gen-demo.h", +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", + out = "gen-demo.cc", +) + +cxx_library( + name = "include", + exported_headers = { + "src/main.rs": ":gen-header", + }, + visibility = ["PUBLIC"], +) diff --git a/third-party/.gitignore b/third-party/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/third-party/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/third-party/BUCK b/third-party/BUCK new file mode 100644 index 0000000..1c1bf3d --- /dev/null +++ b/third-party/BUCK @@ -0,0 +1,243 @@ +# To be generated by Facebook's `reindeer` tool once that is open source. + +rust_library( + name = "anyhow", + srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + visibility = ["PUBLIC"], + features = ["std"], +) + +rust_library( + name = "bitflags", + srcs = glob(["vendor/bitflags-1.2.1/src/**"]), +) + +rust_library( + name = "cc", + srcs = glob(["vendor/cc-1.0.50/src/**"]), + visibility = ["PUBLIC"], +) + +rust_library( + name = "clap", + srcs = glob(["vendor/clap-2.33.0/src/**"]), + edition = "2015", + deps = [ + ":bitflags", + ":textwrap", + ":unicode-width", + ], +) + +rust_library( + name = "codespan", + srcs = glob(["vendor/codespan-0.7.0/src/**"]), + visibility = ["PUBLIC"], + deps = [":unicode-segmentation"], +) + +rust_library( + name = "codespan-reporting", + srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":codespan", + ":termcolor", + ":unicode-width", + ], +) + +rust_library( + name = "heck", + srcs = glob(["vendor/heck-0.3.1/src/**"]), + edition = "2015", + deps = [":unicode-segmentation"], +) + +rust_library( + name = "lazy_static", + srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), +) + +rust_library( + name = "link-cplusplus", + srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + visibility = ["PUBLIC"], +) + +rust_library( + name = "proc-macro-error", + srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + rustc_flags = ["--cfg=use_fallback"], + deps = [ + ":proc-macro-error-attr", + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "proc-macro-error-attr", + srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + proc_macro = True, + deps = [ + ":proc-macro2", + ":quote", + ":rustversion", + ":syn", + ":syn-mid", + ], +) + +rust_library( + name = "proc-macro2", + srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + visibility = ["PUBLIC"], + features = [ + "proc-macro", + "span-locations", + ], + rustc_flags = [ + "--cfg=span_locations", + "--cfg=use_proc_macro", + "--cfg=wrap_proc_macro", + ], + deps = [":unicode-xid"], +) + +rust_library( + name = "quote", + srcs = glob(["vendor/quote-1.0.2/src/**"]), + visibility = ["PUBLIC"], + features = ["proc-macro"], + deps = [":proc-macro2"], +) + +rust_library( + name = "rustversion", + srcs = glob(["vendor/rustversion-1.0.2/src/**"]), + mapped_srcs = { + ":rustversion-buildscript-run": "vendor/rustversion-1.0.2/src/generated", + }, + proc_macro = True, + env = { + "OUT_DIR": "generated", + }, + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_binary( + name = "rustversion-buildscript", + srcs = glob(["vendor/rustversion-1.0.2/build/**"]), + crate_root = "vendor/rustversion-1.0.2/build/build.rs", +) + +genrule( + name = "rustversion-buildscript-run", + cmd = "OUT_DIR=${OUT} $(exe :rustversion-buildscript)", + type = "build.rs", + out = ".", +) + +rust_library( + name = "structopt", + srcs = glob(["vendor/structopt-0.3.9/src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":clap", + ":lazy_static", + ":structopt-derive", + ], +) + +rust_library( + name = "structopt-derive", + srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + proc_macro = True, + deps = [ + ":heck", + ":proc-macro-error", + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "syn", + srcs = glob(["vendor/syn-1.0.14/src/**"]), + visibility = ["PUBLIC"], + features = [ + "clone-impls", + "derive", + "full", + "parsing", + "printing", + "proc-macro", + ], + deps = [ + ":proc-macro2", + ":quote", + ":unicode-xid", + ], +) + +rust_library( + name = "syn-mid", + srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "termcolor", + srcs = glob(["vendor/termcolor-1.1.0/src/**"]), +) + +rust_library( + name = "textwrap", + srcs = glob(["vendor/textwrap-0.11.0/src/**"]), + deps = [":unicode-width"], +) + +rust_library( + name = "thiserror", + srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + visibility = ["PUBLIC"], + deps = [":thiserror-impl"], +) + +rust_library( + name = "thiserror-impl", + srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + proc_macro = True, + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "unicode-segmentation", + srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), + edition = "2015", +) + +rust_library( + name = "unicode-width", + srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), +) + +rust_library( + name = "unicode-xid", + srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), +) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock new file mode 100644 index 0000000..fda2025 --- /dev/null +++ b/third-party/Cargo.lock @@ -0,0 +1,434 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "codespan" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "codespan-reporting" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "657b2c99e1f17bc3e5153808d9f704c8ba6171c3fe45e69fde26e2876156938b" +dependencies = [ + "codespan", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cxx" +version = "0.1.2" +dependencies = [ + "anyhow", + "cc", + "codespan", + "codespan-reporting", + "cxxbridge-macro", + "link-cplusplus", + "proc-macro2", + "quote", + "rustversion", + "syn", + "thiserror", + "trybuild", +] + +[[package]] +name = "cxxbridge-cmd" +version = "0.1.2" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "proc-macro2", + "quote", + "structopt", + "syn", + "thiserror", +] + +[[package]] +name = "cxxbridge-demo" +version = "0.0.0" +dependencies = [ + "cxx", +] + +[[package]] +name = "cxxbridge-macro" +version = "0.1.2" +dependencies = [ + "cxx", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "link-cplusplus" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628cd9d7b5c99cb930617438a3d7896f5eb734647bc2838ded9ca50689507295" +dependencies = [ + "cc", +] + +[[package]] +name = "proc-macro-error" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", + "syn-mid", +] + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "trybuild" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5b3f750c701725331ac78e389b5d143b7d25f6b6ffffd0d419759a9063ac5f" +dependencies = [ + "glob", + "lazy_static", + "serde", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From ab9fa6d08c7a458a243329c40601c46f256f7a57 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 18 2020 09:44:45 +0000 Subject: [PATCH 28/2232] Merge pull request #34 from dtolnay/buck Add buck targets --- diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 0000000..33c15ec --- /dev/null +++ b/.buckconfig @@ -0,0 +1,12 @@ +[project] + # We use some symlinks in the source tree, but they get eliminated by `cargo + # publish` and `cargo vendor` so this allow_symlinks setting should not be + # required downstream. + allow_symlinks = allow + +[cxx] + cxxflags = -std=c++11 + +[rust] + default_edition = 2018 + rustc_flags = -Crelocation-model=dynamic-no-pic --cap-lints=allow diff --git a/.gitignore b/.gitignore index a874449..b403b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/.buckd +/buck-out /Cargo.lock /expand.cc /expand.rs diff --git a/.travis.yml b/.travis.yml index f054344..74f438d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,3 +9,18 @@ os: script: - cargo run --manifest-path demo-rs/Cargo.toml - cargo test + +matrix: + include: + - name: Buck + before_install: + - sudo apt-get install -y openjdk-8-jdk + - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + - wget -O buck.deb https://github.com/facebook/buck/releases/download/v2019.10.17.01/buck.2019.10.17.01_all.deb + - sudo dpkg -i buck.deb + before_script: + - cp third-party/Cargo.lock . + - cargo vendor --versioned-dirs third-party/vendor + script: + - buck build :cxx#check --verbose=0 + - buck run demo-rs --verbose=0 diff --git a/BUCK b/BUCK new file mode 100644 index 0000000..2d12753 --- /dev/null +++ b/BUCK @@ -0,0 +1,60 @@ +rust_library( + name = "cxx", + srcs = glob(["src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":core", + ":macro", + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan", + "//third-party:codespan-reporting", + "//third-party:link-cplusplus", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +rust_binary( + name = "codegen", + srcs = glob(["cmd/src/**"]), + visibility = ["PUBLIC"], + env = { + "CARGO_PKG_AUTHORS": "David Tolnay ", + }, + deps = [ + "//third-party:anyhow", + "//third-party:codespan", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:structopt", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +cxx_library( + name = "core", + srcs = ["src/cxxbridge.cc"], + visibility = ["PUBLIC"], + header_namespace = "cxxbridge", + exported_headers = { + "cxxbridge.h": "include/cxxbridge.h", + }, + exported_linker_flags = ["-lstdc++"], +) + +rust_library( + name = "macro", + srcs = glob(["macro/src/**"]), + proc_macro = True, + crate = "cxxbridge_macro", + deps = [ + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK new file mode 100644 index 0000000..595a18b --- /dev/null +++ b/demo-cxx/BUCK @@ -0,0 +1,16 @@ +cxx_library( + name = "demo-cxx", + srcs = ["demo.cc"], + visibility = ["PUBLIC"], + deps = [ + ":include", + "//demo-rs:include", + ], +) + +cxx_library( + name = "include", + exported_headers = ["demo.h"], + visibility = ["PUBLIC"], + deps = ["//:core"], +) diff --git a/demo-rs/BUCK b/demo-rs/BUCK new file mode 100644 index 0000000..a0f6fb0 --- /dev/null +++ b/demo-rs/BUCK @@ -0,0 +1,42 @@ +rust_binary( + name = "demo-rs", + srcs = glob(["src/**"]), + deps = [ + ":gen", + "//:cxx", + "//demo-cxx:demo-cxx", + ], +) + +cxx_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":include", + "//demo-cxx:include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", + out = "gen-demo.h", +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", + out = "gen-demo.cc", +) + +cxx_library( + name = "include", + exported_headers = { + "src/main.rs": ":gen-header", + }, + visibility = ["PUBLIC"], +) diff --git a/third-party/.gitignore b/third-party/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/third-party/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/third-party/BUCK b/third-party/BUCK new file mode 100644 index 0000000..1c1bf3d --- /dev/null +++ b/third-party/BUCK @@ -0,0 +1,243 @@ +# To be generated by Facebook's `reindeer` tool once that is open source. + +rust_library( + name = "anyhow", + srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + visibility = ["PUBLIC"], + features = ["std"], +) + +rust_library( + name = "bitflags", + srcs = glob(["vendor/bitflags-1.2.1/src/**"]), +) + +rust_library( + name = "cc", + srcs = glob(["vendor/cc-1.0.50/src/**"]), + visibility = ["PUBLIC"], +) + +rust_library( + name = "clap", + srcs = glob(["vendor/clap-2.33.0/src/**"]), + edition = "2015", + deps = [ + ":bitflags", + ":textwrap", + ":unicode-width", + ], +) + +rust_library( + name = "codespan", + srcs = glob(["vendor/codespan-0.7.0/src/**"]), + visibility = ["PUBLIC"], + deps = [":unicode-segmentation"], +) + +rust_library( + name = "codespan-reporting", + srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":codespan", + ":termcolor", + ":unicode-width", + ], +) + +rust_library( + name = "heck", + srcs = glob(["vendor/heck-0.3.1/src/**"]), + edition = "2015", + deps = [":unicode-segmentation"], +) + +rust_library( + name = "lazy_static", + srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), +) + +rust_library( + name = "link-cplusplus", + srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + visibility = ["PUBLIC"], +) + +rust_library( + name = "proc-macro-error", + srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + rustc_flags = ["--cfg=use_fallback"], + deps = [ + ":proc-macro-error-attr", + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "proc-macro-error-attr", + srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + proc_macro = True, + deps = [ + ":proc-macro2", + ":quote", + ":rustversion", + ":syn", + ":syn-mid", + ], +) + +rust_library( + name = "proc-macro2", + srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + visibility = ["PUBLIC"], + features = [ + "proc-macro", + "span-locations", + ], + rustc_flags = [ + "--cfg=span_locations", + "--cfg=use_proc_macro", + "--cfg=wrap_proc_macro", + ], + deps = [":unicode-xid"], +) + +rust_library( + name = "quote", + srcs = glob(["vendor/quote-1.0.2/src/**"]), + visibility = ["PUBLIC"], + features = ["proc-macro"], + deps = [":proc-macro2"], +) + +rust_library( + name = "rustversion", + srcs = glob(["vendor/rustversion-1.0.2/src/**"]), + mapped_srcs = { + ":rustversion-buildscript-run": "vendor/rustversion-1.0.2/src/generated", + }, + proc_macro = True, + env = { + "OUT_DIR": "generated", + }, + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_binary( + name = "rustversion-buildscript", + srcs = glob(["vendor/rustversion-1.0.2/build/**"]), + crate_root = "vendor/rustversion-1.0.2/build/build.rs", +) + +genrule( + name = "rustversion-buildscript-run", + cmd = "OUT_DIR=${OUT} $(exe :rustversion-buildscript)", + type = "build.rs", + out = ".", +) + +rust_library( + name = "structopt", + srcs = glob(["vendor/structopt-0.3.9/src/**"]), + visibility = ["PUBLIC"], + deps = [ + ":clap", + ":lazy_static", + ":structopt-derive", + ], +) + +rust_library( + name = "structopt-derive", + srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + proc_macro = True, + deps = [ + ":heck", + ":proc-macro-error", + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "syn", + srcs = glob(["vendor/syn-1.0.14/src/**"]), + visibility = ["PUBLIC"], + features = [ + "clone-impls", + "derive", + "full", + "parsing", + "printing", + "proc-macro", + ], + deps = [ + ":proc-macro2", + ":quote", + ":unicode-xid", + ], +) + +rust_library( + name = "syn-mid", + srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "termcolor", + srcs = glob(["vendor/termcolor-1.1.0/src/**"]), +) + +rust_library( + name = "textwrap", + srcs = glob(["vendor/textwrap-0.11.0/src/**"]), + deps = [":unicode-width"], +) + +rust_library( + name = "thiserror", + srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + visibility = ["PUBLIC"], + deps = [":thiserror-impl"], +) + +rust_library( + name = "thiserror-impl", + srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + proc_macro = True, + deps = [ + ":proc-macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "unicode-segmentation", + srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), + edition = "2015", +) + +rust_library( + name = "unicode-width", + srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), +) + +rust_library( + name = "unicode-xid", + srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), +) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock new file mode 100644 index 0000000..fda2025 --- /dev/null +++ b/third-party/Cargo.lock @@ -0,0 +1,434 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "codespan" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "codespan-reporting" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "657b2c99e1f17bc3e5153808d9f704c8ba6171c3fe45e69fde26e2876156938b" +dependencies = [ + "codespan", + "termcolor", + "unicode-width", +] + +[[package]] +name = "cxx" +version = "0.1.2" +dependencies = [ + "anyhow", + "cc", + "codespan", + "codespan-reporting", + "cxxbridge-macro", + "link-cplusplus", + "proc-macro2", + "quote", + "rustversion", + "syn", + "thiserror", + "trybuild", +] + +[[package]] +name = "cxxbridge-cmd" +version = "0.1.2" +dependencies = [ + "anyhow", + "codespan", + "codespan-reporting", + "proc-macro2", + "quote", + "structopt", + "syn", + "thiserror", +] + +[[package]] +name = "cxxbridge-demo" +version = "0.0.0" +dependencies = [ + "cxx", +] + +[[package]] +name = "cxxbridge-macro" +version = "0.1.2" +dependencies = [ + "cxx", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "link-cplusplus" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628cd9d7b5c99cb930617438a3d7896f5eb734647bc2838ded9ca50689507295" +dependencies = [ + "cc", +] + +[[package]] +name = "proc-macro-error" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", + "syn-mid", +] + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "trybuild" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5b3f750c701725331ac78e389b5d143b7d25f6b6ffffd0d419759a9063ac5f" +dependencies = [ + "glob", + "lazy_static", + "serde", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From c9f2798772cdb7252b18743a7cb82092fbf8310f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 18 2020 23:31:25 +0000 Subject: [PATCH 29/2232] Set buckversion=last Buck in some environments requires this file for multiplexing across different versions. --- diff --git a/.buckversion b/.buckversion new file mode 100644 index 0000000..b25fa3f --- /dev/null +++ b/.buckversion @@ -0,0 +1 @@ +last From 461712be9f5473439911602c5b887ab013f8a378 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 19 2020 08:22:26 +0000 Subject: [PATCH 30/2232] Add CI build on beta toolchain --- diff --git a/.travis.yml b/.travis.yml index 74f438d..273a6c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: rust -rust: nightly +rust: + - nightly + - beta os: - linux @@ -13,6 +15,7 @@ script: matrix: include: - name: Buck + rust: nightly before_install: - sudo apt-get install -y openjdk-8-jdk - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 From e10910f3c77519bc78db3ebf2105d9f2f983e92f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 19 2020 08:28:54 +0000 Subject: [PATCH 31/2232] Reduce to just one mac builder --- diff --git a/.travis.yml b/.travis.yml index 273a6c4..59b29a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,14 @@ rust: - nightly - beta -os: - - linux - - macos - script: - cargo run --manifest-path demo-rs/Cargo.toml - cargo test matrix: include: + - os: macos + rust: nightly - name: Buck rust: nightly before_install: From b5bc0b4c9a9497dd322ac0e456d8363193af5584 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 22 2020 23:36:13 +0000 Subject: [PATCH 32/2232] Ensure third-party lockfile is adequate for vendor --- diff --git a/.travis.yml b/.travis.yml index 59b29a3..6352e8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ matrix: - sudo dpkg -i buck.deb before_script: - cp third-party/Cargo.lock . - - cargo vendor --versioned-dirs third-party/vendor + - cargo vendor --versioned-dirs --locked third-party/vendor script: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 From c19936ce7473e48808d15e3db28f3b4361167f5a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 00:01:35 +0000 Subject: [PATCH 33/2232] Add bazel targets --- diff --git a/.gitignore b/.gitignore index b403b1d..e7499e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ /.buckd +/bazel-bin +/bazel-cxx +/bazel-out +/bazel-testlogs /buck-out /Cargo.lock /expand.cc diff --git a/.travis.yml b/.travis.yml index 6352e8d..d1fc088 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,14 @@ matrix: script: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 + - name: Bazel + rust: nightly + before_install: + - wget -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh + - chmod +x install.sh + - ./install.sh --user + before_script: + - cp third-party/Cargo.lock . + - cargo vendor --versioned-dirs --locked third-party/vendor + script: + - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..b48c930 --- /dev/null +++ b/BUILD @@ -0,0 +1,63 @@ +load("//:build/rust.bzl", "rust_binary", "rust_library") + +rust_library( + name = "cxx", + srcs = glob(["src/**/*.rs"]), + data = ["src/gen/include/cxxbridge.h"], + visibility = ["//visibility:public"], + deps = [ + ":core_lib", + ":cxxbridge_macro", + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan", + "//third-party:codespan_reporting", + "//third-party:link_cplusplus", + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +rust_binary( + name = "codegen", + srcs = glob(["cmd/src/**/*.rs"]), + data = ["cmd/src/gen/include/cxxbridge.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:codespan", + "//third-party:codespan_reporting", + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:structopt", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +cc_library( + name = "core", + hdrs = ["include/cxxbridge.h"], + include_prefix = "cxxbridge", + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) + +cc_library( + name = "core_lib", + srcs = ["src/cxxbridge.cc"], + hdrs = ["include/cxxbridge.h"], +) + +rust_library( + name = "cxxbridge_macro", + srcs = glob(["macro/src/**"]), + crate_type = "proc-macro", + deps = [ + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..26866e8 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,32 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_rules_rust", + sha256 = "3d3faa85e49ebf4d26c40075549a17739d636360064b94a9d481b37ace0add82", + strip_prefix = "rules_rust-6e87304c834c30b9c9f585cad19f30e7045281d7", + # Master branch as of 2020-02-22 + url = "https://github.com/bazelbuild/rules_rust/archive/6e87304c834c30b9c9f585cad19f30e7045281d7.tar.gz", +) + +http_archive( + name = "bazel_skylib", + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + ], +) + +load("@io_bazel_rules_rust//:workspace.bzl", "bazel_version") + +bazel_version(name = "bazel_version") + +load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") + +rust_repository_set( + name = "rust_1_42_beta", + exec_triple = "x86_64-unknown-linux-gnu", + extra_target_triples = [], + iso_date = "2020-02-08", + version = "beta", +) diff --git a/build/rust.bzl b/build/rust.bzl new file mode 100644 index 0000000..c2dade7 --- /dev/null +++ b/build/rust.bzl @@ -0,0 +1,19 @@ +load( + "@io_bazel_rules_rust//rust:rust.bzl", + _rust_binary = "rust_binary", + _rust_library = "rust_library", +) + +def rust_binary(edition = "2018", **kwargs): + _rust_binary(edition = edition, **kwargs) + +def third_party_rust_binary(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_binary(rustc_flags = rustc_flags, **kwargs) + +def rust_library(edition = "2018", **kwargs): + _rust_library(edition = edition, **kwargs) + +def third_party_rust_library(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_library(rustc_flags = rustc_flags, **kwargs) diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD new file mode 100644 index 0000000..da97cfa --- /dev/null +++ b/demo-cxx/BUILD @@ -0,0 +1,16 @@ +cc_library( + name = "demo-cxx", + srcs = ["demo.cc"], + visibility = ["//visibility:public"], + deps = [ + ":include", + "//demo-rs:include", + ], +) + +cc_library( + name = "include", + hdrs = ["demo.h"], + visibility = ["//visibility:public"], + deps = ["//:core"], +) diff --git a/demo-rs/BUILD b/demo-rs/BUILD new file mode 100644 index 0000000..fb781b6 --- /dev/null +++ b/demo-rs/BUILD @@ -0,0 +1,43 @@ +load("//:build/rust.bzl", "rust_binary", "rust_library") + +rust_binary( + name = "demo_rs", + srcs = glob(["src/**"]), + deps = [ + ":gen", + "//:cxx", + "//demo-cxx", + ], +) + +cc_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":include", + "//demo-cxx:include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + outs = ["main.rs"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + outs = ["gen-demo.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "demo-rs/src", + visibility = ["//visibility:public"], +) diff --git a/third-party/BUILD b/third-party/BUILD new file mode 100644 index 0000000..11b4257 --- /dev/null +++ b/third-party/BUILD @@ -0,0 +1,249 @@ +load( + "//:build/rust.bzl", + rust_binary = "third_party_rust_binary", + rust_library = "third_party_rust_library", +) +load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") + +rust_library( + name = "anyhow", + srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + crate_features = ["std"], + visibility = ["//visibility:public"], +) + +rust_library( + name = "bitflags", + srcs = glob(["vendor/bitflags-1.2.1/src/**"]), +) + +rust_library( + name = "cc", + srcs = glob(["vendor/cc-1.0.50/src/**"]), + visibility = ["//visibility:public"], +) + +rust_library( + name = "clap", + srcs = glob(["vendor/clap-2.33.0/src/**"]), + edition = "2015", + deps = [ + ":bitflags", + ":textwrap", + ":unicode_width", + ], +) + +rust_library( + name = "codespan", + srcs = glob(["vendor/codespan-0.7.0/src/**"]), + visibility = ["//visibility:public"], + deps = [":unicode_segmentation"], +) + +rust_library( + name = "codespan_reporting", + srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + visibility = ["//visibility:public"], + deps = [ + ":codespan", + ":termcolor", + ":unicode_width", + ], +) + +rust_library( + name = "heck", + srcs = glob(["vendor/heck-0.3.1/src/**"]), + edition = "2015", + deps = [":unicode_segmentation"], +) + +rust_library( + name = "lazy_static", + srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), +) + +rust_library( + name = "link_cplusplus", + srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + visibility = ["//visibility:public"], +) + +rust_library( + name = "proc_macro_error", + srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + rustc_flags = ["--cfg=use_fallback"], + deps = [ + ":proc_macro2", + ":proc_macro_error_attr", + ":quote", + ":syn", + ], +) + +rust_library( + name = "proc_macro_error_attr", + srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + crate_type = "proc-macro", + deps = [ + ":proc_macro2", + ":quote", + ":rustversion", + ":syn", + ":syn_mid", + ], +) + +rust_library( + name = "proc_macro2", + srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + crate_features = [ + "proc-macro", + "span-locations", + ], + rustc_flags = [ + "--cfg=span_locations", + "--cfg=use_proc_macro", + "--cfg=wrap_proc_macro", + ], + visibility = ["//visibility:public"], + deps = [":unicode_xid"], +) + +rust_library( + name = "quote", + srcs = glob(["vendor/quote-1.0.2/src/**"]), + crate_features = ["proc-macro"], + visibility = ["//visibility:public"], + deps = [":proc_macro2"], +) + +rust_library( + name = "rustversion", + srcs = glob(["vendor/rustversion-1.0.2/src/**"]), + crate_type = "proc-macro", + out_dir_tar = ":rustversion_buildscript_outdir", + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_binary( + name = "rustversion_buildscript", + srcs = glob(["vendor/rustversion-1.0.2/build/**"]), + crate_root = "vendor/rustversion-1.0.2/build/build.rs", +) + +pkg_tar( + name = "rustversion_buildscript_outdir", + srcs = [":rustversion_buildscript_run"], + extension = "tar.gz", +) + +genrule( + name = "rustversion_buildscript_run", + outs = ["version.rs"], + cmd = "OUT_DIR=$(@D) $(location :rustversion_buildscript)", + tools = [":rustversion_buildscript"], +) + +rust_library( + name = "structopt", + srcs = glob(["vendor/structopt-0.3.9/src/**"]), + visibility = ["//visibility:public"], + deps = [ + ":clap", + ":lazy_static", + ":structopt_derive", + ], +) + +rust_library( + name = "structopt_derive", + srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + crate_type = "proc-macro", + deps = [ + ":heck", + ":proc_macro2", + ":proc_macro_error", + ":quote", + ":syn", + ], +) + +rust_library( + name = "syn", + srcs = glob(["vendor/syn-1.0.14/src/**"]), + crate_features = [ + "clone-impls", + "derive", + "full", + "parsing", + "printing", + "proc-macro", + ], + visibility = ["//visibility:public"], + deps = [ + ":proc_macro2", + ":quote", + ":unicode_xid", + ], +) + +rust_library( + name = "syn_mid", + srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "termcolor", + srcs = glob(["vendor/termcolor-1.1.0/src/**"]), +) + +rust_library( + name = "textwrap", + srcs = glob(["vendor/textwrap-0.11.0/src/**"]), + deps = [":unicode_width"], +) + +rust_library( + name = "thiserror", + srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + visibility = ["//visibility:public"], + deps = [":thiserror_impl"], +) + +rust_library( + name = "thiserror_impl", + srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + crate_type = "proc-macro", + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "unicode_segmentation", + srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), + edition = "2015", +) + +rust_library( + name = "unicode_width", + srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), +) + +rust_library( + name = "unicode_xid", + srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), +) From 67a658fddfa21090d6180f56c74c03ebbd6eab6c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 00:10:43 +0000 Subject: [PATCH 34/2232] Merge pull request #36 from dtolnay/bazel Add bazel targets --- diff --git a/.gitignore b/.gitignore index b403b1d..e7499e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ /.buckd +/bazel-bin +/bazel-cxx +/bazel-out +/bazel-testlogs /buck-out /Cargo.lock /expand.cc diff --git a/.travis.yml b/.travis.yml index 6352e8d..d1fc088 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,3 +25,14 @@ matrix: script: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 + - name: Bazel + rust: nightly + before_install: + - wget -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh + - chmod +x install.sh + - ./install.sh --user + before_script: + - cp third-party/Cargo.lock . + - cargo vendor --versioned-dirs --locked third-party/vendor + script: + - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..b48c930 --- /dev/null +++ b/BUILD @@ -0,0 +1,63 @@ +load("//:build/rust.bzl", "rust_binary", "rust_library") + +rust_library( + name = "cxx", + srcs = glob(["src/**/*.rs"]), + data = ["src/gen/include/cxxbridge.h"], + visibility = ["//visibility:public"], + deps = [ + ":core_lib", + ":cxxbridge_macro", + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan", + "//third-party:codespan_reporting", + "//third-party:link_cplusplus", + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +rust_binary( + name = "codegen", + srcs = glob(["cmd/src/**/*.rs"]), + data = ["cmd/src/gen/include/cxxbridge.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:codespan", + "//third-party:codespan_reporting", + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:structopt", + "//third-party:syn", + "//third-party:thiserror", + ], +) + +cc_library( + name = "core", + hdrs = ["include/cxxbridge.h"], + include_prefix = "cxxbridge", + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) + +cc_library( + name = "core_lib", + srcs = ["src/cxxbridge.cc"], + hdrs = ["include/cxxbridge.h"], +) + +rust_library( + name = "cxxbridge_macro", + srcs = glob(["macro/src/**"]), + crate_type = "proc-macro", + deps = [ + "//third-party:proc_macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..26866e8 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,32 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_rules_rust", + sha256 = "3d3faa85e49ebf4d26c40075549a17739d636360064b94a9d481b37ace0add82", + strip_prefix = "rules_rust-6e87304c834c30b9c9f585cad19f30e7045281d7", + # Master branch as of 2020-02-22 + url = "https://github.com/bazelbuild/rules_rust/archive/6e87304c834c30b9c9f585cad19f30e7045281d7.tar.gz", +) + +http_archive( + name = "bazel_skylib", + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", + ], +) + +load("@io_bazel_rules_rust//:workspace.bzl", "bazel_version") + +bazel_version(name = "bazel_version") + +load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") + +rust_repository_set( + name = "rust_1_42_beta", + exec_triple = "x86_64-unknown-linux-gnu", + extra_target_triples = [], + iso_date = "2020-02-08", + version = "beta", +) diff --git a/build/rust.bzl b/build/rust.bzl new file mode 100644 index 0000000..c2dade7 --- /dev/null +++ b/build/rust.bzl @@ -0,0 +1,19 @@ +load( + "@io_bazel_rules_rust//rust:rust.bzl", + _rust_binary = "rust_binary", + _rust_library = "rust_library", +) + +def rust_binary(edition = "2018", **kwargs): + _rust_binary(edition = edition, **kwargs) + +def third_party_rust_binary(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_binary(rustc_flags = rustc_flags, **kwargs) + +def rust_library(edition = "2018", **kwargs): + _rust_library(edition = edition, **kwargs) + +def third_party_rust_library(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_library(rustc_flags = rustc_flags, **kwargs) diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD new file mode 100644 index 0000000..da97cfa --- /dev/null +++ b/demo-cxx/BUILD @@ -0,0 +1,16 @@ +cc_library( + name = "demo-cxx", + srcs = ["demo.cc"], + visibility = ["//visibility:public"], + deps = [ + ":include", + "//demo-rs:include", + ], +) + +cc_library( + name = "include", + hdrs = ["demo.h"], + visibility = ["//visibility:public"], + deps = ["//:core"], +) diff --git a/demo-rs/BUILD b/demo-rs/BUILD new file mode 100644 index 0000000..fb781b6 --- /dev/null +++ b/demo-rs/BUILD @@ -0,0 +1,43 @@ +load("//:build/rust.bzl", "rust_binary", "rust_library") + +rust_binary( + name = "demo_rs", + srcs = glob(["src/**"]), + deps = [ + ":gen", + "//:cxx", + "//demo-cxx", + ], +) + +cc_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":include", + "//demo-cxx:include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + outs = ["main.rs"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + outs = ["gen-demo.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "demo-rs/src", + visibility = ["//visibility:public"], +) diff --git a/third-party/BUILD b/third-party/BUILD new file mode 100644 index 0000000..11b4257 --- /dev/null +++ b/third-party/BUILD @@ -0,0 +1,249 @@ +load( + "//:build/rust.bzl", + rust_binary = "third_party_rust_binary", + rust_library = "third_party_rust_library", +) +load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") + +rust_library( + name = "anyhow", + srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + crate_features = ["std"], + visibility = ["//visibility:public"], +) + +rust_library( + name = "bitflags", + srcs = glob(["vendor/bitflags-1.2.1/src/**"]), +) + +rust_library( + name = "cc", + srcs = glob(["vendor/cc-1.0.50/src/**"]), + visibility = ["//visibility:public"], +) + +rust_library( + name = "clap", + srcs = glob(["vendor/clap-2.33.0/src/**"]), + edition = "2015", + deps = [ + ":bitflags", + ":textwrap", + ":unicode_width", + ], +) + +rust_library( + name = "codespan", + srcs = glob(["vendor/codespan-0.7.0/src/**"]), + visibility = ["//visibility:public"], + deps = [":unicode_segmentation"], +) + +rust_library( + name = "codespan_reporting", + srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + visibility = ["//visibility:public"], + deps = [ + ":codespan", + ":termcolor", + ":unicode_width", + ], +) + +rust_library( + name = "heck", + srcs = glob(["vendor/heck-0.3.1/src/**"]), + edition = "2015", + deps = [":unicode_segmentation"], +) + +rust_library( + name = "lazy_static", + srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), +) + +rust_library( + name = "link_cplusplus", + srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + visibility = ["//visibility:public"], +) + +rust_library( + name = "proc_macro_error", + srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + rustc_flags = ["--cfg=use_fallback"], + deps = [ + ":proc_macro2", + ":proc_macro_error_attr", + ":quote", + ":syn", + ], +) + +rust_library( + name = "proc_macro_error_attr", + srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + crate_type = "proc-macro", + deps = [ + ":proc_macro2", + ":quote", + ":rustversion", + ":syn", + ":syn_mid", + ], +) + +rust_library( + name = "proc_macro2", + srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + crate_features = [ + "proc-macro", + "span-locations", + ], + rustc_flags = [ + "--cfg=span_locations", + "--cfg=use_proc_macro", + "--cfg=wrap_proc_macro", + ], + visibility = ["//visibility:public"], + deps = [":unicode_xid"], +) + +rust_library( + name = "quote", + srcs = glob(["vendor/quote-1.0.2/src/**"]), + crate_features = ["proc-macro"], + visibility = ["//visibility:public"], + deps = [":proc_macro2"], +) + +rust_library( + name = "rustversion", + srcs = glob(["vendor/rustversion-1.0.2/src/**"]), + crate_type = "proc-macro", + out_dir_tar = ":rustversion_buildscript_outdir", + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_binary( + name = "rustversion_buildscript", + srcs = glob(["vendor/rustversion-1.0.2/build/**"]), + crate_root = "vendor/rustversion-1.0.2/build/build.rs", +) + +pkg_tar( + name = "rustversion_buildscript_outdir", + srcs = [":rustversion_buildscript_run"], + extension = "tar.gz", +) + +genrule( + name = "rustversion_buildscript_run", + outs = ["version.rs"], + cmd = "OUT_DIR=$(@D) $(location :rustversion_buildscript)", + tools = [":rustversion_buildscript"], +) + +rust_library( + name = "structopt", + srcs = glob(["vendor/structopt-0.3.9/src/**"]), + visibility = ["//visibility:public"], + deps = [ + ":clap", + ":lazy_static", + ":structopt_derive", + ], +) + +rust_library( + name = "structopt_derive", + srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + crate_type = "proc-macro", + deps = [ + ":heck", + ":proc_macro2", + ":proc_macro_error", + ":quote", + ":syn", + ], +) + +rust_library( + name = "syn", + srcs = glob(["vendor/syn-1.0.14/src/**"]), + crate_features = [ + "clone-impls", + "derive", + "full", + "parsing", + "printing", + "proc-macro", + ], + visibility = ["//visibility:public"], + deps = [ + ":proc_macro2", + ":quote", + ":unicode_xid", + ], +) + +rust_library( + name = "syn_mid", + srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "termcolor", + srcs = glob(["vendor/termcolor-1.1.0/src/**"]), +) + +rust_library( + name = "textwrap", + srcs = glob(["vendor/textwrap-0.11.0/src/**"]), + deps = [":unicode_width"], +) + +rust_library( + name = "thiserror", + srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + visibility = ["//visibility:public"], + deps = [":thiserror_impl"], +) + +rust_library( + name = "thiserror_impl", + srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + crate_type = "proc-macro", + deps = [ + ":proc_macro2", + ":quote", + ":syn", + ], +) + +rust_library( + name = "unicode_segmentation", + srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), + edition = "2015", +) + +rust_library( + name = "unicode_width", + srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), +) + +rust_library( + name = "unicode_xid", + srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), +) From c6244698ad3a4abf312f38b74b8d3d62e0bb53b8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 02:51:30 +0000 Subject: [PATCH 35/2232] Add a bazel rust toolchain for macOS --- diff --git a/WORKSPACE b/WORKSPACE index 26866e8..e6f96d2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,9 +24,17 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_42_beta", + name = "rust_1_42_beta_linux", exec_triple = "x86_64-unknown-linux-gnu", extra_target_triples = [], iso_date = "2020-02-08", version = "beta", ) + +rust_repository_set( + name = "rust_1_42_beta_darwin", + exec_triple = "x86_64-apple-darwin", + extra_target_triples = [], + iso_date = "2020-02-08", + version = "beta", +) From 54a53df768cbf99fd3cd4565938a8a99612e6ba8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 03:30:23 +0000 Subject: [PATCH 36/2232] Update vendored dependencies Nothing important, but just working out this workflow. --- diff --git a/third-party/BUCK b/third-party/BUCK index 1c1bf3d..5c0e90d 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -170,7 +170,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.14/src/**"]), + srcs = glob(["vendor/syn-1.0.15/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 11b4257..ceae8e2 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -176,7 +176,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.14/src/**"]), + srcs = glob(["vendor/syn-1.0.15/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index fda2025..3844959 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -160,9 +160,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "link-cplusplus" @@ -297,9 +297,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5" dependencies = [ "proc-macro2", "quote", @@ -366,9 +366,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5b3f750c701725331ac78e389b5d143b7d25f6b6ffffd0d419759a9063ac5f" +checksum = "26ff1b18659a2218332848d76ad1c867ce4c6ee37b085e6bc8de9a6d11401220" dependencies = [ "glob", "lazy_static", From 97c72100391fdc26bc872e55c68b62fb6ec8e039 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 03:31:49 +0000 Subject: [PATCH 37/2232] Add test suite beyond the existing demo --- diff --git a/Cargo.toml b/Cargo.toml index b120173..fdfadc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,9 @@ thiserror = "1.0" cc = "1.0.49" [dev-dependencies] +cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = "1.0" +trybuild = "1.0.21" [workspace] -members = ["cmd", "demo-rs", "macro"] +members = ["cmd", "demo-rs", "macro", "tests/ffi"] diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml new file mode 100644 index 0000000..c84df61 --- /dev/null +++ b/tests/ffi/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cxx-test-suite" +version = "0.0.0" +edition = "2018" +publish = false + +[lib] +path = "lib.rs" + +[dependencies] +cxx = { path = "../.." } + +[build-dependencies] +cxx = { path = "../.." } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs new file mode 100644 index 0000000..5c1bec3 --- /dev/null +++ b/tests/ffi/build.rs @@ -0,0 +1,11 @@ +fn main() { + if cfg!(trybuild) { + return; + } + + cxx::Build::new() + .bridge("lib.rs") + .file("tests.cc") + .flag("-std=c++11") + .compile("cxx-test-suite"); +} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs new file mode 100644 index 0000000..3ae732a --- /dev/null +++ b/tests/ffi/lib.rs @@ -0,0 +1,6 @@ +#[cxx::bridge(namespace = tests)] +pub mod ffi { + extern "C" { + include!("tests/ffi/tests.h"); + } +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc new file mode 100644 index 0000000..6996d77 --- /dev/null +++ b/tests/ffi/tests.cc @@ -0,0 +1,6 @@ +#include "tests/ffi/lib.rs" +#include "tests/ffi/tests.h" + +namespace tests { + +} // namespace tests diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h new file mode 100644 index 0000000..61f11d5 --- /dev/null +++ b/tests/ffi/tests.h @@ -0,0 +1,6 @@ +#pragma once +#include "include/cxxbridge.h" + +namespace tests { + +} // namespace tests From ad5b8afd9b5a3a03d8423b22d5006d8446b4ada4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 03:31:49 +0000 Subject: [PATCH 38/2232] Add exhaustive coverage of signature kinds --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 3ae732a..168261d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -1,6 +1,115 @@ +use cxx::{CxxString, UniquePtr}; + #[cxx::bridge(namespace = tests)] pub mod ffi { + struct Shared { + z: usize, + } + extern "C" { include!("tests/ffi/tests.h"); + + type C; + + fn c_return_primitive() -> usize; + fn c_return_shared() -> Shared; + //TODO fn c_return_box() -> Box; + fn c_return_unique_ptr() -> UniquePtr; + fn c_return_ref(shared: &Shared) -> &usize; + fn c_return_str(shared: &Shared) -> &str; + fn c_return_rust_string() -> String; + fn c_return_unique_ptr_string() -> UniquePtr; + + fn c_take_primitive(n: usize); + fn c_take_shared(shared: Shared); + fn c_take_box(r: Box); + fn c_take_unique_ptr(c: UniquePtr); + //TODO fn c_take_ref_r(r: &R); + fn c_take_ref_c(c: &C); + fn c_take_str(s: &str); + fn c_take_rust_string(s: String); + fn c_take_unique_ptr_string(s: UniquePtr); + } + + extern "Rust" { + type R; + + fn r_return_primitive() -> usize; + fn r_return_shared() -> Shared; + //TODO fn r_return_box() -> Box; + //TODO fn r_return_unique_ptr() -> UniquePtr; + fn r_return_ref(shared: &Shared) -> &usize; + fn r_return_str(shared: &Shared) -> &str; + fn r_return_rust_string() -> String; + //TODO fn r_return_unique_ptr_string() -> UniquePtr; + + fn r_take_primitive(n: usize); + fn r_take_shared(shared: Shared); + fn r_take_box(r: Box); + fn r_take_unique_ptr(c: UniquePtr); + fn r_take_ref_r(r: &R); + fn r_take_ref_c(c: &C); + fn r_take_str(s: &str); + fn r_take_rust_string(s: String); + fn r_take_unique_ptr_string(s: UniquePtr); } } + +type R = (); + +fn r_return_primitive() -> usize { + 2020 +} + +fn r_return_shared() -> ffi::Shared { + ffi::Shared { z: 2020 } +} + +fn r_return_ref(shared: &ffi::Shared) -> &usize { + &shared.z +} + +fn r_return_str(shared: &ffi::Shared) -> &str { + let _ = shared; + "2020" +} + +fn r_return_rust_string() -> String { + "2020".to_owned() +} + +fn r_take_primitive(n: usize) { + let _ = n; +} + +fn r_take_shared(shared: ffi::Shared) { + let _ = shared; +} + +fn r_take_box(r: Box) { + let _ = r; +} + +fn r_take_unique_ptr(c: UniquePtr) { + let _ = c; +} + +fn r_take_ref_r(r: &R) { + let _ = r; +} + +fn r_take_ref_c(c: &ffi::C) { + let _ = c; +} + +fn r_take_str(s: &str) { + let _ = s; +} + +fn r_take_rust_string(s: String) { + let _ = s; +} + +fn r_take_unique_ptr_string(s: UniquePtr) { + let _ = s; +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 6996d77..ecfafd0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -3,4 +3,71 @@ namespace tests { +C::C(size_t n) : n(n) {} + +size_t c_return_primitive() { + return 2020; +} + +Shared c_return_shared() { + return Shared{2020}; +} + +std::unique_ptr c_return_unique_ptr() { + return std::unique_ptr(new C{2020}); +} + +const size_t &c_return_ref(const Shared &shared) { + return shared.z; +} + +cxxbridge::RustStr c_return_str(const Shared &shared) { + (void)shared; + return "2020"; +} + +cxxbridge::RustString c_return_rust_string() { + return "2020"; +} + +std::unique_ptr c_return_unique_ptr_string() { + return std::unique_ptr(new std::string("2020")); +} + +void c_take_primitive(size_t n) { + (void)n; +} + +void c_take_shared(Shared shared) { + (void)shared; +} + +void c_take_box(cxxbridge::RustBox r) { + (void)r; +} + +void c_take_unique_ptr(std::unique_ptr c) { + (void)c; +} + +void c_take_ref_r(const R &r) { + (void)r; +} + +void c_take_ref_c(const C &c) { + (void)c; +} + +void c_take_str(cxxbridge::RustStr s) { + (void)s; +} + +void c_take_rust_string(cxxbridge::RustString s) { + (void)s; +} + +void c_take_unique_ptr_string(std::unique_ptr s) { + (void)s; +} + } // namespace tests diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 61f11d5..8510946 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -1,6 +1,38 @@ #pragma once #include "include/cxxbridge.h" +#include +#include namespace tests { +struct R; +struct Shared; + +class C { +public: + C(size_t n); + +private: + size_t n; +}; + +size_t c_return_primitive(); +Shared c_return_shared(); +cxxbridge::RustBox c_return_box(); +std::unique_ptr c_return_unique_ptr(); +const size_t &c_return_ref(const Shared &shared); +cxxbridge::RustStr c_return_str(const Shared &shared); +cxxbridge::RustString c_return_rust_string(); +std::unique_ptr c_return_unique_ptr_string(); + +void c_take_primitive(size_t n); +void c_take_shared(Shared shared); +void c_take_box(cxxbridge::RustBox r); +void c_take_unique_ptr(std::unique_ptr c); +void c_take_ref_r(const R &r); +void c_take_ref_c(const C &c); +void c_take_str(cxxbridge::RustStr s); +void c_take_rust_string(cxxbridge::RustString s); +void c_take_unique_ptr_string(std::unique_ptr s); + } // namespace tests From b871577737a9bea9a200766d16a3ab9c1489176e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 03:31:49 +0000 Subject: [PATCH 39/2232] Format with clang-format --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index ecfafd0..26c14fe 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,73 +1,47 @@ -#include "tests/ffi/lib.rs" #include "tests/ffi/tests.h" +#include "tests/ffi/lib.rs" namespace tests { C::C(size_t n) : n(n) {} -size_t c_return_primitive() { - return 2020; -} +size_t c_return_primitive() { return 2020; } -Shared c_return_shared() { - return Shared{2020}; -} +Shared c_return_shared() { return Shared{2020}; } std::unique_ptr c_return_unique_ptr() { return std::unique_ptr(new C{2020}); } -const size_t &c_return_ref(const Shared &shared) { - return shared.z; -} +const size_t &c_return_ref(const Shared &shared) { return shared.z; } cxxbridge::RustStr c_return_str(const Shared &shared) { (void)shared; return "2020"; } -cxxbridge::RustString c_return_rust_string() { - return "2020"; -} +cxxbridge::RustString c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); } -void c_take_primitive(size_t n) { - (void)n; -} +void c_take_primitive(size_t n) { (void)n; } -void c_take_shared(Shared shared) { - (void)shared; -} +void c_take_shared(Shared shared) { (void)shared; } -void c_take_box(cxxbridge::RustBox r) { - (void)r; -} +void c_take_box(cxxbridge::RustBox r) { (void)r; } -void c_take_unique_ptr(std::unique_ptr c) { - (void)c; -} +void c_take_unique_ptr(std::unique_ptr c) { (void)c; } -void c_take_ref_r(const R &r) { - (void)r; -} +void c_take_ref_r(const R &r) { (void)r; } -void c_take_ref_c(const C &c) { - (void)c; -} +void c_take_ref_c(const C &c) { (void)c; } -void c_take_str(cxxbridge::RustStr s) { - (void)s; -} +void c_take_str(cxxbridge::RustStr s) { (void)s; } -void c_take_rust_string(cxxbridge::RustString s) { - (void)s; -} +void c_take_rust_string(cxxbridge::RustString s) { (void)s; } -void c_take_unique_ptr_string(std::unique_ptr s) { - (void)s; -} +void c_take_unique_ptr_string(std::unique_ptr s) { (void)s; } } // namespace tests From 3fd7f56275674a5ce22ef987be7596d1c27195f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 04:02:02 +0000 Subject: [PATCH 40/2232] Test rust calling to c++ --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 26c14fe..cfe07ab 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,10 +1,14 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" +extern "C" void cxx_test_suite_set_correct(); + namespace tests { C::C(size_t n) : n(n) {} +size_t C::get() const { return this->n; } + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -26,22 +30,53 @@ std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); } -void c_take_primitive(size_t n) { (void)n; } +void c_take_primitive(size_t n) { + if (n == 2020) { + cxx_test_suite_set_correct(); + } +} -void c_take_shared(Shared shared) { (void)shared; } +void c_take_shared(Shared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} -void c_take_box(cxxbridge::RustBox r) { (void)r; } +void c_take_box(cxxbridge::RustBox r) { + (void)r; + cxx_test_suite_set_correct(); +} -void c_take_unique_ptr(std::unique_ptr c) { (void)c; } +void c_take_unique_ptr(std::unique_ptr c) { + if (c->get() == 2020) { + cxx_test_suite_set_correct(); + } +} void c_take_ref_r(const R &r) { (void)r; } -void c_take_ref_c(const C &c) { (void)c; } +void c_take_ref_c(const C &c) { + if (c.get() == 2020) { + cxx_test_suite_set_correct(); + } +} -void c_take_str(cxxbridge::RustStr s) { (void)s; } +void c_take_str(cxxbridge::RustStr s) { + if (std::string(s) == "2020") { + cxx_test_suite_set_correct(); + } +} -void c_take_rust_string(cxxbridge::RustString s) { (void)s; } +void c_take_rust_string(cxxbridge::RustString s) { + if (std::string(s) == "2020") { + cxx_test_suite_set_correct(); + } +} -void c_take_unique_ptr_string(std::unique_ptr s) { (void)s; } +void c_take_unique_ptr_string(std::unique_ptr s) { + if (*s == "2020") { + cxx_test_suite_set_correct(); + } +} } // namespace tests diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 8510946..14e019f 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -11,6 +11,7 @@ struct Shared; class C { public: C(size_t n); + size_t get() const; private: size_t n; diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..9f595f7 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,55 @@ +use cxx_test_suite::ffi; +use std::cell::Cell; + +thread_local! { + static CORRECT: Cell = Cell::new(false); +} + +#[no_mangle] +extern "C" fn cxx_test_suite_set_correct() { + CORRECT.with(|correct| correct.set(true)); +} + +#[test] +fn test_c_return() { + let shared = ffi::Shared { z: 2020 }; + + assert_eq!(2020, ffi::c_return_primitive()); + assert_eq!(2020, ffi::c_return_shared().z); + ffi::c_return_unique_ptr(); + assert_eq!(2020, *ffi::c_return_ref(&shared)); + assert_eq!("2020", ffi::c_return_str(&shared)); + assert_eq!("2020", ffi::c_return_rust_string()); + assert_eq!( + "2020", + ffi::c_return_unique_ptr_string() + .as_ref() + .unwrap() + .to_str() + .unwrap() + ); +} + +#[test] +fn test_c_take() { + macro_rules! check { + ($run:expr) => {{ + CORRECT.with(|correct| correct.set(false)); + $run; + assert!(CORRECT.with(|correct| correct.get()), stringify!($run)); + }}; + } + + let unique_ptr = ffi::c_return_unique_ptr(); + + check!(ffi::c_take_primitive(2020)); + check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); + check!(ffi::c_take_box(Box::new(()))); + check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); + check!(ffi::c_take_unique_ptr(unique_ptr)); + check!(ffi::c_take_str("2020")); + check!(ffi::c_take_rust_string("2020".to_owned())); + check!(ffi::c_take_unique_ptr_string( + ffi::c_return_unique_ptr_string() + )); +} From f306da4dc713a3ea78edc10e0d73d4252cde73a0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 06:42:35 +0000 Subject: [PATCH 41/2232] Test c++ calling to rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 168261d..098253a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -79,11 +79,11 @@ fn r_return_rust_string() -> String { } fn r_take_primitive(n: usize) { - let _ = n; + assert_eq!(n, 2020); } fn r_take_shared(shared: ffi::Shared) { - let _ = shared; + assert_eq!(shared.z, 2020); } fn r_take_box(r: Box) { @@ -103,13 +103,13 @@ fn r_take_ref_c(c: &ffi::C) { } fn r_take_str(s: &str) { - let _ = s; + assert_eq!(s, "2020"); } fn r_take_rust_string(s: String) { - let _ = s; + assert_eq!(s, "2020"); } fn r_take_unique_ptr_string(s: UniquePtr) { - let _ = s; + assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index cfe07ab..d27df41 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -79,4 +79,33 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +extern "C" const char *cxx_run_test() noexcept { +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + return "Assertion failed: `" #x "`, " __FILE__ ":" TOSTRING(__LINE__); \ + } \ + } while (false) + + ASSERT(r_return_primitive() == 2020); + ASSERT(r_return_shared().z == 2020); + ASSERT(r_return_ref(Shared{2020}) == 2020); + ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); + ASSERT(std::string(r_return_rust_string()) == "2020"); + + r_take_primitive(2020); + r_take_shared(Shared{2020}); + r_take_unique_ptr(std::unique_ptr(new C{2020})); + r_take_ref_c(C{2020}); + r_take_str(cxxbridge::RustStr("2020")); + // TODO r_take_rust_string(cxxbridge::RustString("2020")); + r_take_unique_ptr_string( + std::unique_ptr(new std::string("2020"))); + + cxx_test_suite_set_correct(); + return nullptr; +} + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index 9f595f7..28e5c3b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,6 @@ use cxx_test_suite::ffi; use std::cell::Cell; +use std::ffi::CStr; thread_local! { static CORRECT: Cell = Cell::new(false); @@ -10,6 +11,14 @@ extern "C" fn cxx_test_suite_set_correct() { CORRECT.with(|correct| correct.set(true)); } +macro_rules! check { + ($run:expr) => {{ + CORRECT.with(|correct| correct.set(false)); + $run; + assert!(CORRECT.with(|correct| correct.get()), stringify!($run)); + }}; +} + #[test] fn test_c_return() { let shared = ffi::Shared { z: 2020 }; @@ -32,14 +41,6 @@ fn test_c_return() { #[test] fn test_c_take() { - macro_rules! check { - ($run:expr) => {{ - CORRECT.with(|correct| correct.set(false)); - $run; - assert!(CORRECT.with(|correct| correct.get()), stringify!($run)); - }}; - } - let unique_ptr = ffi::c_return_unique_ptr(); check!(ffi::c_take_primitive(2020)); @@ -53,3 +54,18 @@ fn test_c_take() { ffi::c_return_unique_ptr_string() )); } + +#[test] +fn test_c_call_r() { + fn cxx_run_test() { + extern "C" { + fn cxx_run_test() -> *const i8; + } + let failure = unsafe { cxx_run_test() }; + if !failure.is_null() { + let msg = unsafe { CStr::from_ptr(failure) }; + eprintln!("{}", msg.to_string_lossy()); + } + } + check!(cxx_run_test()); +} From c9bf95703faea0ee08c2e90e4fc6028a8a7e7e19 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 06:53:03 +0000 Subject: [PATCH 42/2232] Update lockfile to include test suite crate --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3844959..5f2a1ca 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -81,6 +81,7 @@ dependencies = [ "cc", "codespan", "codespan-reporting", + "cxx-test-suite", "cxxbridge-macro", "link-cplusplus", "proc-macro2", @@ -92,6 +93,13 @@ dependencies = [ ] [[package]] +name = "cxx-test-suite" +version = "0.0.0" +dependencies = [ + "cxx", +] + +[[package]] name = "cxxbridge-cmd" version = "0.1.2" dependencies = [ From d1010bdb390103a258a393bab872ca776337ee65 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 07:03:10 +0000 Subject: [PATCH 43/2232] Merge pull request #38 from dtolnay/suite Add test suite covering various permutations of signatures --- diff --git a/Cargo.toml b/Cargo.toml index b120173..fdfadc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,9 @@ thiserror = "1.0" cc = "1.0.49" [dev-dependencies] +cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = "1.0" +trybuild = "1.0.21" [workspace] -members = ["cmd", "demo-rs", "macro"] +members = ["cmd", "demo-rs", "macro", "tests/ffi"] diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml new file mode 100644 index 0000000..c84df61 --- /dev/null +++ b/tests/ffi/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cxx-test-suite" +version = "0.0.0" +edition = "2018" +publish = false + +[lib] +path = "lib.rs" + +[dependencies] +cxx = { path = "../.." } + +[build-dependencies] +cxx = { path = "../.." } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs new file mode 100644 index 0000000..5c1bec3 --- /dev/null +++ b/tests/ffi/build.rs @@ -0,0 +1,11 @@ +fn main() { + if cfg!(trybuild) { + return; + } + + cxx::Build::new() + .bridge("lib.rs") + .file("tests.cc") + .flag("-std=c++11") + .compile("cxx-test-suite"); +} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs new file mode 100644 index 0000000..098253a --- /dev/null +++ b/tests/ffi/lib.rs @@ -0,0 +1,115 @@ +use cxx::{CxxString, UniquePtr}; + +#[cxx::bridge(namespace = tests)] +pub mod ffi { + struct Shared { + z: usize, + } + + extern "C" { + include!("tests/ffi/tests.h"); + + type C; + + fn c_return_primitive() -> usize; + fn c_return_shared() -> Shared; + //TODO fn c_return_box() -> Box; + fn c_return_unique_ptr() -> UniquePtr; + fn c_return_ref(shared: &Shared) -> &usize; + fn c_return_str(shared: &Shared) -> &str; + fn c_return_rust_string() -> String; + fn c_return_unique_ptr_string() -> UniquePtr; + + fn c_take_primitive(n: usize); + fn c_take_shared(shared: Shared); + fn c_take_box(r: Box); + fn c_take_unique_ptr(c: UniquePtr); + //TODO fn c_take_ref_r(r: &R); + fn c_take_ref_c(c: &C); + fn c_take_str(s: &str); + fn c_take_rust_string(s: String); + fn c_take_unique_ptr_string(s: UniquePtr); + } + + extern "Rust" { + type R; + + fn r_return_primitive() -> usize; + fn r_return_shared() -> Shared; + //TODO fn r_return_box() -> Box; + //TODO fn r_return_unique_ptr() -> UniquePtr; + fn r_return_ref(shared: &Shared) -> &usize; + fn r_return_str(shared: &Shared) -> &str; + fn r_return_rust_string() -> String; + //TODO fn r_return_unique_ptr_string() -> UniquePtr; + + fn r_take_primitive(n: usize); + fn r_take_shared(shared: Shared); + fn r_take_box(r: Box); + fn r_take_unique_ptr(c: UniquePtr); + fn r_take_ref_r(r: &R); + fn r_take_ref_c(c: &C); + fn r_take_str(s: &str); + fn r_take_rust_string(s: String); + fn r_take_unique_ptr_string(s: UniquePtr); + } +} + +type R = (); + +fn r_return_primitive() -> usize { + 2020 +} + +fn r_return_shared() -> ffi::Shared { + ffi::Shared { z: 2020 } +} + +fn r_return_ref(shared: &ffi::Shared) -> &usize { + &shared.z +} + +fn r_return_str(shared: &ffi::Shared) -> &str { + let _ = shared; + "2020" +} + +fn r_return_rust_string() -> String { + "2020".to_owned() +} + +fn r_take_primitive(n: usize) { + assert_eq!(n, 2020); +} + +fn r_take_shared(shared: ffi::Shared) { + assert_eq!(shared.z, 2020); +} + +fn r_take_box(r: Box) { + let _ = r; +} + +fn r_take_unique_ptr(c: UniquePtr) { + let _ = c; +} + +fn r_take_ref_r(r: &R) { + let _ = r; +} + +fn r_take_ref_c(c: &ffi::C) { + let _ = c; +} + +fn r_take_str(s: &str) { + assert_eq!(s, "2020"); +} + +fn r_take_rust_string(s: String) { + assert_eq!(s, "2020"); +} + +fn r_take_unique_ptr_string(s: UniquePtr) { + assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc new file mode 100644 index 0000000..d27df41 --- /dev/null +++ b/tests/ffi/tests.cc @@ -0,0 +1,111 @@ +#include "tests/ffi/tests.h" +#include "tests/ffi/lib.rs" + +extern "C" void cxx_test_suite_set_correct(); + +namespace tests { + +C::C(size_t n) : n(n) {} + +size_t C::get() const { return this->n; } + +size_t c_return_primitive() { return 2020; } + +Shared c_return_shared() { return Shared{2020}; } + +std::unique_ptr c_return_unique_ptr() { + return std::unique_ptr(new C{2020}); +} + +const size_t &c_return_ref(const Shared &shared) { return shared.z; } + +cxxbridge::RustStr c_return_str(const Shared &shared) { + (void)shared; + return "2020"; +} + +cxxbridge::RustString c_return_rust_string() { return "2020"; } + +std::unique_ptr c_return_unique_ptr_string() { + return std::unique_ptr(new std::string("2020")); +} + +void c_take_primitive(size_t n) { + if (n == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_shared(Shared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_box(cxxbridge::RustBox r) { + (void)r; + cxx_test_suite_set_correct(); +} + +void c_take_unique_ptr(std::unique_ptr c) { + if (c->get() == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_ref_r(const R &r) { (void)r; } + +void c_take_ref_c(const C &c) { + if (c.get() == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_str(cxxbridge::RustStr s) { + if (std::string(s) == "2020") { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_string(cxxbridge::RustString s) { + if (std::string(s) == "2020") { + cxx_test_suite_set_correct(); + } +} + +void c_take_unique_ptr_string(std::unique_ptr s) { + if (*s == "2020") { + cxx_test_suite_set_correct(); + } +} + +extern "C" const char *cxx_run_test() noexcept { +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + return "Assertion failed: `" #x "`, " __FILE__ ":" TOSTRING(__LINE__); \ + } \ + } while (false) + + ASSERT(r_return_primitive() == 2020); + ASSERT(r_return_shared().z == 2020); + ASSERT(r_return_ref(Shared{2020}) == 2020); + ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); + ASSERT(std::string(r_return_rust_string()) == "2020"); + + r_take_primitive(2020); + r_take_shared(Shared{2020}); + r_take_unique_ptr(std::unique_ptr(new C{2020})); + r_take_ref_c(C{2020}); + r_take_str(cxxbridge::RustStr("2020")); + // TODO r_take_rust_string(cxxbridge::RustString("2020")); + r_take_unique_ptr_string( + std::unique_ptr(new std::string("2020"))); + + cxx_test_suite_set_correct(); + return nullptr; +} + +} // namespace tests diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h new file mode 100644 index 0000000..14e019f --- /dev/null +++ b/tests/ffi/tests.h @@ -0,0 +1,39 @@ +#pragma once +#include "include/cxxbridge.h" +#include +#include + +namespace tests { + +struct R; +struct Shared; + +class C { +public: + C(size_t n); + size_t get() const; + +private: + size_t n; +}; + +size_t c_return_primitive(); +Shared c_return_shared(); +cxxbridge::RustBox c_return_box(); +std::unique_ptr c_return_unique_ptr(); +const size_t &c_return_ref(const Shared &shared); +cxxbridge::RustStr c_return_str(const Shared &shared); +cxxbridge::RustString c_return_rust_string(); +std::unique_ptr c_return_unique_ptr_string(); + +void c_take_primitive(size_t n); +void c_take_shared(Shared shared); +void c_take_box(cxxbridge::RustBox r); +void c_take_unique_ptr(std::unique_ptr c); +void c_take_ref_r(const R &r); +void c_take_ref_c(const C &c); +void c_take_str(cxxbridge::RustStr s); +void c_take_rust_string(cxxbridge::RustString s); +void c_take_unique_ptr_string(std::unique_ptr s); + +} // namespace tests diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..28e5c3b --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,71 @@ +use cxx_test_suite::ffi; +use std::cell::Cell; +use std::ffi::CStr; + +thread_local! { + static CORRECT: Cell = Cell::new(false); +} + +#[no_mangle] +extern "C" fn cxx_test_suite_set_correct() { + CORRECT.with(|correct| correct.set(true)); +} + +macro_rules! check { + ($run:expr) => {{ + CORRECT.with(|correct| correct.set(false)); + $run; + assert!(CORRECT.with(|correct| correct.get()), stringify!($run)); + }}; +} + +#[test] +fn test_c_return() { + let shared = ffi::Shared { z: 2020 }; + + assert_eq!(2020, ffi::c_return_primitive()); + assert_eq!(2020, ffi::c_return_shared().z); + ffi::c_return_unique_ptr(); + assert_eq!(2020, *ffi::c_return_ref(&shared)); + assert_eq!("2020", ffi::c_return_str(&shared)); + assert_eq!("2020", ffi::c_return_rust_string()); + assert_eq!( + "2020", + ffi::c_return_unique_ptr_string() + .as_ref() + .unwrap() + .to_str() + .unwrap() + ); +} + +#[test] +fn test_c_take() { + let unique_ptr = ffi::c_return_unique_ptr(); + + check!(ffi::c_take_primitive(2020)); + check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); + check!(ffi::c_take_box(Box::new(()))); + check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); + check!(ffi::c_take_unique_ptr(unique_ptr)); + check!(ffi::c_take_str("2020")); + check!(ffi::c_take_rust_string("2020".to_owned())); + check!(ffi::c_take_unique_ptr_string( + ffi::c_return_unique_ptr_string() + )); +} + +#[test] +fn test_c_call_r() { + fn cxx_run_test() { + extern "C" { + fn cxx_run_test() -> *const i8; + } + let failure = unsafe { cxx_run_test() }; + if !failure.is_null() { + let msg = unsafe { CStr::from_ptr(failure) }; + eprintln!("{}", msg.to_string_lossy()); + } + } + check!(cxx_run_test()); +} diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3844959..5f2a1ca 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -81,6 +81,7 @@ dependencies = [ "cc", "codespan", "codespan-reporting", + "cxx-test-suite", "cxxbridge-macro", "link-cplusplus", "proc-macro2", @@ -92,6 +93,13 @@ dependencies = [ ] [[package]] +name = "cxx-test-suite" +version = "0.0.0" +dependencies = [ + "cxx", +] + +[[package]] name = "cxxbridge-cmd" version = "0.1.2" dependencies = [ From e3e0a7129111d81f77424fe321ca1635de33d20b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 08:18:57 +0000 Subject: [PATCH 44/2232] Use the public cxxbridge.h include path --- diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 14e019f..87aac67 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -1,5 +1,5 @@ #pragma once -#include "include/cxxbridge.h" +#include "cxxbridge/cxxbridge.h" #include #include From 9bc613e7dbba8041a130deb61d11aa1689f5d818 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 08:36:44 +0000 Subject: [PATCH 45/2232] Add test suite targets for buck and bazel --- diff --git a/.travis.yml b/.travis.yml index d1fc088..6d7f255 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ matrix: script: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 + - buck test ... --verbose=0 - name: Bazel rust: nightly before_install: @@ -36,3 +37,4 @@ matrix: - cargo vendor --versioned-dirs --locked third-party/vendor script: - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress + - bazel test ... --verbose_failures --noshow_progress diff --git a/build/rust.bzl b/build/rust.bzl index c2dade7..3ef4d91 100644 --- a/build/rust.bzl +++ b/build/rust.bzl @@ -2,6 +2,7 @@ load( "@io_bazel_rules_rust//rust:rust.bzl", _rust_binary = "rust_binary", _rust_library = "rust_library", + _rust_test = "rust_test", ) def rust_binary(edition = "2018", **kwargs): @@ -17,3 +18,6 @@ def rust_library(edition = "2018", **kwargs): def third_party_rust_library(rustc_flags = [], **kwargs): rustc_flags = rustc_flags + ["--cap-lints=allow"] rust_library(rustc_flags = rustc_flags, **kwargs) + +def rust_test(edition = "2018", **kwargs): + _rust_test(edition = edition, **kwargs) diff --git a/tests/BUCK b/tests/BUCK new file mode 100644 index 0000000..81f74de --- /dev/null +++ b/tests/BUCK @@ -0,0 +1,42 @@ +rust_test( + name = "test", + srcs = ["test.rs"], + deps = [":ffi"], +) + +rust_library( + name = "ffi", + srcs = ["ffi/lib.rs"], + crate = "cxx_test_suite", + deps = [ + ":impl", + "//:cxx", + ], +) + +cxx_library( + name = "impl", + srcs = [ + "ffi/tests.cc", + ":gen-source", + ], + headers = { + "ffi/lib.rs": ":gen-header", + "ffi/tests.h": "ffi/tests.h", + }, + deps = ["//:core"], +) + +genrule( + name = "gen-header", + srcs = ["ffi/lib.rs"], + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + out = "gen.h", +) + +genrule( + name = "gen-source", + srcs = ["ffi/lib.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + out = "gen.cc", +) diff --git a/tests/BUILD b/tests/BUILD new file mode 100644 index 0000000..a88cde5 --- /dev/null +++ b/tests/BUILD @@ -0,0 +1,51 @@ +load("//:build/rust.bzl", "rust_library", "rust_test") + +rust_test( + name = "test", + srcs = ["test.rs"], + deps = [":cxx_test_suite"], +) + +rust_library( + name = "cxx_test_suite", + srcs = ["ffi/lib.rs"], + deps = [ + ":impl", + "//:cxx", + ], +) + +cc_library( + name = "impl", + srcs = [ + "ffi/tests.cc", + ":gen-source", + ], + hdrs = ["ffi/tests.h"], + deps = [ + ":include", + "//:core", + ], +) + +genrule( + name = "gen-header", + srcs = ["ffi/lib.rs"], + outs = ["lib.rs"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["ffi/lib.rs"], + outs = ["gen.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "tests/ffi", +) From 54875798eca132136a7b0281b084627c3b14d9d0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 08:46:22 +0000 Subject: [PATCH 46/2232] Merge pull request #39 from dtolnay/suite Add test suite targets for buck and bazel --- diff --git a/.travis.yml b/.travis.yml index d1fc088..6d7f255 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ matrix: script: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 + - buck test ... --verbose=0 - name: Bazel rust: nightly before_install: @@ -36,3 +37,4 @@ matrix: - cargo vendor --versioned-dirs --locked third-party/vendor script: - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress + - bazel test ... --verbose_failures --noshow_progress diff --git a/build/rust.bzl b/build/rust.bzl index c2dade7..3ef4d91 100644 --- a/build/rust.bzl +++ b/build/rust.bzl @@ -2,6 +2,7 @@ load( "@io_bazel_rules_rust//rust:rust.bzl", _rust_binary = "rust_binary", _rust_library = "rust_library", + _rust_test = "rust_test", ) def rust_binary(edition = "2018", **kwargs): @@ -17,3 +18,6 @@ def rust_library(edition = "2018", **kwargs): def third_party_rust_library(rustc_flags = [], **kwargs): rustc_flags = rustc_flags + ["--cap-lints=allow"] rust_library(rustc_flags = rustc_flags, **kwargs) + +def rust_test(edition = "2018", **kwargs): + _rust_test(edition = edition, **kwargs) diff --git a/tests/BUCK b/tests/BUCK new file mode 100644 index 0000000..81f74de --- /dev/null +++ b/tests/BUCK @@ -0,0 +1,42 @@ +rust_test( + name = "test", + srcs = ["test.rs"], + deps = [":ffi"], +) + +rust_library( + name = "ffi", + srcs = ["ffi/lib.rs"], + crate = "cxx_test_suite", + deps = [ + ":impl", + "//:cxx", + ], +) + +cxx_library( + name = "impl", + srcs = [ + "ffi/tests.cc", + ":gen-source", + ], + headers = { + "ffi/lib.rs": ":gen-header", + "ffi/tests.h": "ffi/tests.h", + }, + deps = ["//:core"], +) + +genrule( + name = "gen-header", + srcs = ["ffi/lib.rs"], + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + out = "gen.h", +) + +genrule( + name = "gen-source", + srcs = ["ffi/lib.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + out = "gen.cc", +) diff --git a/tests/BUILD b/tests/BUILD new file mode 100644 index 0000000..a88cde5 --- /dev/null +++ b/tests/BUILD @@ -0,0 +1,51 @@ +load("//:build/rust.bzl", "rust_library", "rust_test") + +rust_test( + name = "test", + srcs = ["test.rs"], + deps = [":cxx_test_suite"], +) + +rust_library( + name = "cxx_test_suite", + srcs = ["ffi/lib.rs"], + deps = [ + ":impl", + "//:cxx", + ], +) + +cc_library( + name = "impl", + srcs = [ + "ffi/tests.cc", + ":gen-source", + ], + hdrs = ["ffi/tests.h"], + deps = [ + ":include", + "//:core", + ], +) + +genrule( + name = "gen-header", + srcs = ["ffi/lib.rs"], + outs = ["lib.rs"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["ffi/lib.rs"], + outs = ["gen.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "tests/ffi", +) From 908385887bde9a6309ffa770fcfaf9db19f323fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 08:57:03 +0000 Subject: [PATCH 47/2232] Protect from ADL in Box construction and assignment --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index a0d9dbb..0bfe805 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -66,7 +66,7 @@ public: RustBox(RustBox &&other) noexcept : repr(other.repr) { other.repr = 0; } RustBox(const T &val) { this->uninit(); - new (this->deref_mut()) T(val); + ::new (this->deref_mut()) T(val); } RustBox &operator=(const RustBox &other) { if (this != &other) { @@ -74,7 +74,7 @@ public: **this = *other; } else { this->uninit(); - new (this->deref_mut()) T(*other); + ::new (this->deref_mut()) T(*other); } } return *this; From 2fc07c11a831beceaac1e0ab7d36cfffcf94ddfd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 23 2020 09:06:28 +0000 Subject: [PATCH 48/2232] Merge pull request #40 from dtolnay/box Protect from ADL in Box construction and assignment --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index a0d9dbb..0bfe805 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -66,7 +66,7 @@ public: RustBox(RustBox &&other) noexcept : repr(other.repr) { other.repr = 0; } RustBox(const T &val) { this->uninit(); - new (this->deref_mut()) T(val); + ::new (this->deref_mut()) T(val); } RustBox &operator=(const RustBox &other) { if (this != &other) { @@ -74,7 +74,7 @@ public: **this = *other; } else { this->uninit(); - new (this->deref_mut()) T(*other); + ::new (this->deref_mut()) T(*other); } } return *this; From bce77ba9fe44f3b45c72333e9244a0517c689311 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 24 2020 09:54:27 +0000 Subject: [PATCH 49/2232] Add travis build on windows --- diff --git a/.travis.yml b/.travis.yml index 6d7f255..9534d50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,12 @@ matrix: include: - os: macos rust: nightly + - os: windows + rust: nightly + before_script: + # windows is bad at symlinks + - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax + - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src - name: Buck rust: nightly before_install: From 2a1eaac049ca759f962ea7cfea594f45b9b8c832 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 24 2020 10:02:58 +0000 Subject: [PATCH 50/2232] Accept non-\n line endings On Windows, include_str brings in cxxbridge.h with \r\n line endings. --- diff --git a/gen/include.rs b/gen/include.rs index d4ff758..ef741af 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -1,8 +1,8 @@ pub static HEADER: &str = include_str!("include/cxxbridge.h"); pub fn get(guard: &str) -> &'static str { - let ifndef = format!("#ifndef {}\n", guard); - let endif = format!("#endif // {}\n", guard); + let ifndef = format!("#ifndef {}", guard); + let endif = format!("#endif // {}", guard); let begin = HEADER.find(&ifndef); let end = HEADER.find(&endif); if let (Some(begin), Some(end)) = (begin, end) { From 44806f760fcf1605e07ceee9e05e63d42621685d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 24 2020 10:08:10 +0000 Subject: [PATCH 51/2232] Ignore windows build for now --- diff --git a/.travis.yml b/.travis.yml index 9534d50..b238cff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,3 +44,5 @@ matrix: script: - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress + allow_failures: + - os: windows # https://github.com/dtolnay/cxx/issues/25 From 3fdda34015fcf79356918eab6f142ed5ec614651 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 00:47:56 +0000 Subject: [PATCH 52/2232] Pull in io_bazel_rules_rust update to support hyphens --- diff --git a/WORKSPACE b/WORKSPACE index e6f96d2..2feb345 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "3d3faa85e49ebf4d26c40075549a17739d636360064b94a9d481b37ace0add82", - strip_prefix = "rules_rust-6e87304c834c30b9c9f585cad19f30e7045281d7", - # Master branch as of 2020-02-22 - url = "https://github.com/bazelbuild/rules_rust/archive/6e87304c834c30b9c9f585cad19f30e7045281d7.tar.gz", + sha256 = "b7ac870f4cab1cd7e56fd2cbe303f63d78d21cc1a6e3922f21887d373c090e20", + strip_prefix = "rules_rust-5a679d418955a122798f42c7bb67c55ca68a2493", + # Master branch as of 2020-02-24 + url = "https://github.com/dtolnay/rules_rust/archive/5a679d418955a122798f42c7bb67c55ca68a2493.tar.gz", ) http_archive( From 671dff87d6f51ae3d39b73368044e5614636a9e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 00:48:03 +0000 Subject: [PATCH 53/2232] Preserve hyphens in third-party crate names --- diff --git a/.travis.yml b/.travis.yml index b238cff..0248fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ matrix: - cp third-party/Cargo.lock . - cargo vendor --versioned-dirs --locked third-party/vendor script: - - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress + - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress allow_failures: - os: windows # https://github.com/dtolnay/cxx/issues/25 diff --git a/BUILD b/BUILD index b48c930..5d463b2 100644 --- a/BUILD +++ b/BUILD @@ -6,14 +6,14 @@ rust_library( data = ["src/gen/include/cxxbridge.h"], visibility = ["//visibility:public"], deps = [ - ":core_lib", - ":cxxbridge_macro", + ":core-lib", + ":cxxbridge-macro", "//third-party:anyhow", "//third-party:cc", "//third-party:codespan", - "//third-party:codespan_reporting", - "//third-party:link_cplusplus", - "//third-party:proc_macro2", + "//third-party:codespan-reporting", + "//third-party:link-cplusplus", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", "//third-party:thiserror", @@ -28,8 +28,8 @@ rust_binary( deps = [ "//third-party:anyhow", "//third-party:codespan", - "//third-party:codespan_reporting", - "//third-party:proc_macro2", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:structopt", "//third-party:syn", @@ -46,17 +46,17 @@ cc_library( ) cc_library( - name = "core_lib", + name = "core-lib", srcs = ["src/cxxbridge.cc"], hdrs = ["include/cxxbridge.h"], ) rust_library( - name = "cxxbridge_macro", + name = "cxxbridge-macro", srcs = glob(["macro/src/**"]), crate_type = "proc-macro", deps = [ - "//third-party:proc_macro2", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", ], diff --git a/demo-rs/BUILD b/demo-rs/BUILD index fb781b6..674b3d9 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -1,7 +1,7 @@ load("//:build/rust.bzl", "rust_binary", "rust_library") rust_binary( - name = "demo_rs", + name = "demo-rs", srcs = glob(["src/**"]), deps = [ ":gen", diff --git a/third-party/BUILD b/third-party/BUILD index ceae8e2..6792a98 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -30,7 +30,7 @@ rust_library( deps = [ ":bitflags", ":textwrap", - ":unicode_width", + ":unicode-width", ], ) @@ -38,17 +38,17 @@ rust_library( name = "codespan", srcs = glob(["vendor/codespan-0.7.0/src/**"]), visibility = ["//visibility:public"], - deps = [":unicode_segmentation"], + deps = [":unicode-segmentation"], ) rust_library( - name = "codespan_reporting", + name = "codespan-reporting", srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), visibility = ["//visibility:public"], deps = [ ":codespan", ":termcolor", - ":unicode_width", + ":unicode-width", ], ) @@ -56,7 +56,7 @@ rust_library( name = "heck", srcs = glob(["vendor/heck-0.3.1/src/**"]), edition = "2015", - deps = [":unicode_segmentation"], + deps = [":unicode-segmentation"], ) rust_library( @@ -65,38 +65,38 @@ rust_library( ) rust_library( - name = "link_cplusplus", + name = "link-cplusplus", srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), visibility = ["//visibility:public"], ) rust_library( - name = "proc_macro_error", + name = "proc-macro-error", srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ - ":proc_macro2", - ":proc_macro_error_attr", + ":proc-macro2", + ":proc-macro-error-attr", ":quote", ":syn", ], ) rust_library( - name = "proc_macro_error_attr", + name = "proc-macro-error-attr", srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), crate_type = "proc-macro", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":rustversion", ":syn", - ":syn_mid", + ":syn-mid", ], ) rust_library( - name = "proc_macro2", + name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), crate_features = [ "proc-macro", @@ -108,7 +108,7 @@ rust_library( "--cfg=wrap_proc_macro", ], visibility = ["//visibility:public"], - deps = [":unicode_xid"], + deps = [":unicode-xid"], ) rust_library( @@ -116,7 +116,7 @@ rust_library( srcs = glob(["vendor/quote-1.0.2/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], - deps = [":proc_macro2"], + deps = [":proc-macro2"], ) rust_library( @@ -125,7 +125,7 @@ rust_library( crate_type = "proc-macro", out_dir_tar = ":rustversion_buildscript_outdir", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], @@ -157,18 +157,18 @@ rust_library( deps = [ ":clap", ":lazy_static", - ":structopt_derive", + ":structopt-derive", ], ) rust_library( - name = "structopt_derive", + name = "structopt-derive", srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), crate_type = "proc-macro", deps = [ ":heck", - ":proc_macro2", - ":proc_macro_error", + ":proc-macro2", + ":proc-macro-error", ":quote", ":syn", ], @@ -187,17 +187,17 @@ rust_library( ], visibility = ["//visibility:public"], deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", - ":unicode_xid", + ":unicode-xid", ], ) rust_library( - name = "syn_mid", + name = "syn-mid", srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], @@ -211,39 +211,39 @@ rust_library( rust_library( name = "textwrap", srcs = glob(["vendor/textwrap-0.11.0/src/**"]), - deps = [":unicode_width"], + deps = [":unicode-width"], ) rust_library( name = "thiserror", srcs = glob(["vendor/thiserror-1.0.11/src/**"]), visibility = ["//visibility:public"], - deps = [":thiserror_impl"], + deps = [":thiserror-impl"], ) rust_library( - name = "thiserror_impl", + name = "thiserror-impl", srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), crate_type = "proc-macro", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], ) rust_library( - name = "unicode_segmentation", + name = "unicode-segmentation", srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), edition = "2015", ) rust_library( - name = "unicode_width", + name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), ) rust_library( - name = "unicode_xid", + name = "unicode-xid", srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), ) From 7ce723cae039f91a86090efd231bbd8a3fcee5fd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 04:15:32 +0000 Subject: [PATCH 54/2232] Merge pull request #37 from dtolnay/hyphen Preserve hyphens in bazel third-party crate names --- diff --git a/.travis.yml b/.travis.yml index b238cff..0248fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ matrix: - cp third-party/Cargo.lock . - cargo vendor --versioned-dirs --locked third-party/vendor script: - - bazel run demo-rs:demo_rs --verbose_failures --noshow_progress + - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress allow_failures: - os: windows # https://github.com/dtolnay/cxx/issues/25 diff --git a/BUILD b/BUILD index b48c930..5d463b2 100644 --- a/BUILD +++ b/BUILD @@ -6,14 +6,14 @@ rust_library( data = ["src/gen/include/cxxbridge.h"], visibility = ["//visibility:public"], deps = [ - ":core_lib", - ":cxxbridge_macro", + ":core-lib", + ":cxxbridge-macro", "//third-party:anyhow", "//third-party:cc", "//third-party:codespan", - "//third-party:codespan_reporting", - "//third-party:link_cplusplus", - "//third-party:proc_macro2", + "//third-party:codespan-reporting", + "//third-party:link-cplusplus", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", "//third-party:thiserror", @@ -28,8 +28,8 @@ rust_binary( deps = [ "//third-party:anyhow", "//third-party:codespan", - "//third-party:codespan_reporting", - "//third-party:proc_macro2", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:structopt", "//third-party:syn", @@ -46,17 +46,17 @@ cc_library( ) cc_library( - name = "core_lib", + name = "core-lib", srcs = ["src/cxxbridge.cc"], hdrs = ["include/cxxbridge.h"], ) rust_library( - name = "cxxbridge_macro", + name = "cxxbridge-macro", srcs = glob(["macro/src/**"]), crate_type = "proc-macro", deps = [ - "//third-party:proc_macro2", + "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", ], diff --git a/WORKSPACE b/WORKSPACE index e6f96d2..2feb345 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "3d3faa85e49ebf4d26c40075549a17739d636360064b94a9d481b37ace0add82", - strip_prefix = "rules_rust-6e87304c834c30b9c9f585cad19f30e7045281d7", - # Master branch as of 2020-02-22 - url = "https://github.com/bazelbuild/rules_rust/archive/6e87304c834c30b9c9f585cad19f30e7045281d7.tar.gz", + sha256 = "b7ac870f4cab1cd7e56fd2cbe303f63d78d21cc1a6e3922f21887d373c090e20", + strip_prefix = "rules_rust-5a679d418955a122798f42c7bb67c55ca68a2493", + # Master branch as of 2020-02-24 + url = "https://github.com/dtolnay/rules_rust/archive/5a679d418955a122798f42c7bb67c55ca68a2493.tar.gz", ) http_archive( diff --git a/demo-rs/BUILD b/demo-rs/BUILD index fb781b6..674b3d9 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -1,7 +1,7 @@ load("//:build/rust.bzl", "rust_binary", "rust_library") rust_binary( - name = "demo_rs", + name = "demo-rs", srcs = glob(["src/**"]), deps = [ ":gen", diff --git a/third-party/BUILD b/third-party/BUILD index ceae8e2..6792a98 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -30,7 +30,7 @@ rust_library( deps = [ ":bitflags", ":textwrap", - ":unicode_width", + ":unicode-width", ], ) @@ -38,17 +38,17 @@ rust_library( name = "codespan", srcs = glob(["vendor/codespan-0.7.0/src/**"]), visibility = ["//visibility:public"], - deps = [":unicode_segmentation"], + deps = [":unicode-segmentation"], ) rust_library( - name = "codespan_reporting", + name = "codespan-reporting", srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), visibility = ["//visibility:public"], deps = [ ":codespan", ":termcolor", - ":unicode_width", + ":unicode-width", ], ) @@ -56,7 +56,7 @@ rust_library( name = "heck", srcs = glob(["vendor/heck-0.3.1/src/**"]), edition = "2015", - deps = [":unicode_segmentation"], + deps = [":unicode-segmentation"], ) rust_library( @@ -65,38 +65,38 @@ rust_library( ) rust_library( - name = "link_cplusplus", + name = "link-cplusplus", srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), visibility = ["//visibility:public"], ) rust_library( - name = "proc_macro_error", + name = "proc-macro-error", srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ - ":proc_macro2", - ":proc_macro_error_attr", + ":proc-macro2", + ":proc-macro-error-attr", ":quote", ":syn", ], ) rust_library( - name = "proc_macro_error_attr", + name = "proc-macro-error-attr", srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), crate_type = "proc-macro", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":rustversion", ":syn", - ":syn_mid", + ":syn-mid", ], ) rust_library( - name = "proc_macro2", + name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), crate_features = [ "proc-macro", @@ -108,7 +108,7 @@ rust_library( "--cfg=wrap_proc_macro", ], visibility = ["//visibility:public"], - deps = [":unicode_xid"], + deps = [":unicode-xid"], ) rust_library( @@ -116,7 +116,7 @@ rust_library( srcs = glob(["vendor/quote-1.0.2/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], - deps = [":proc_macro2"], + deps = [":proc-macro2"], ) rust_library( @@ -125,7 +125,7 @@ rust_library( crate_type = "proc-macro", out_dir_tar = ":rustversion_buildscript_outdir", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], @@ -157,18 +157,18 @@ rust_library( deps = [ ":clap", ":lazy_static", - ":structopt_derive", + ":structopt-derive", ], ) rust_library( - name = "structopt_derive", + name = "structopt-derive", srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), crate_type = "proc-macro", deps = [ ":heck", - ":proc_macro2", - ":proc_macro_error", + ":proc-macro2", + ":proc-macro-error", ":quote", ":syn", ], @@ -187,17 +187,17 @@ rust_library( ], visibility = ["//visibility:public"], deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", - ":unicode_xid", + ":unicode-xid", ], ) rust_library( - name = "syn_mid", + name = "syn-mid", srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], @@ -211,39 +211,39 @@ rust_library( rust_library( name = "textwrap", srcs = glob(["vendor/textwrap-0.11.0/src/**"]), - deps = [":unicode_width"], + deps = [":unicode-width"], ) rust_library( name = "thiserror", srcs = glob(["vendor/thiserror-1.0.11/src/**"]), visibility = ["//visibility:public"], - deps = [":thiserror_impl"], + deps = [":thiserror-impl"], ) rust_library( - name = "thiserror_impl", + name = "thiserror-impl", srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), crate_type = "proc-macro", deps = [ - ":proc_macro2", + ":proc-macro2", ":quote", ":syn", ], ) rust_library( - name = "unicode_segmentation", + name = "unicode-segmentation", srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), edition = "2015", ) rust_library( - name = "unicode_width", + name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), ) rust_library( - name = "unicode_xid", + name = "unicode-xid", srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), ) From fa1a2bdb5a93c6b174bfa3e21796462c73e4781f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 05:58:17 +0000 Subject: [PATCH 55/2232] Remove reliance on fs::canonicalize on Windows --- diff --git a/src/paths.rs b/src/paths.rs index f253939..e318664 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -44,7 +44,7 @@ fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { fn relative_to_parent_of_target_dir(original: &Path) -> Result { let target_dir = target_dir()?; let mut outer = target_dir.parent().unwrap(); - let original = original.canonicalize()?; + let original = canonicalize(original)?; loop { if let Ok(suffix) = original.strip_prefix(outer) { return Ok(suffix.to_owned()); @@ -71,7 +71,7 @@ pub(crate) fn include_dir() -> Result { } fn target_dir() -> Result { - let mut dir = out_dir()?.canonicalize()?; + let mut dir = out_dir().and_then(canonicalize)?; loop { if dir.ends_with("target") { return Ok(dir); @@ -81,3 +81,17 @@ fn target_dir() -> Result { } } } + +#[cfg(not(windows))] +fn canonicalize(path: impl AsRef) -> Result { + Ok(fs::canonicalize(path)?) +} + +#[cfg(windows)] +fn canonicalize(path: impl AsRef) -> Result { + // Real fs::canonicalize on Windows produces UNC paths which cl.exe is + // unable to handle in includes. Use a poor approximation instead. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + Ok(env::current_dir()?.join(path)) +} From 25221c110bba3d2da12b4ba03007b7c54ada5347 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 06:20:17 +0000 Subject: [PATCH 56/2232] Merge pull request #41 from dtolnay/canonicalize Remove reliance on fs::canonicalize on Windows --- diff --git a/src/paths.rs b/src/paths.rs index f253939..e318664 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -44,7 +44,7 @@ fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { fn relative_to_parent_of_target_dir(original: &Path) -> Result { let target_dir = target_dir()?; let mut outer = target_dir.parent().unwrap(); - let original = original.canonicalize()?; + let original = canonicalize(original)?; loop { if let Ok(suffix) = original.strip_prefix(outer) { return Ok(suffix.to_owned()); @@ -71,7 +71,7 @@ pub(crate) fn include_dir() -> Result { } fn target_dir() -> Result { - let mut dir = out_dir()?.canonicalize()?; + let mut dir = out_dir().and_then(canonicalize)?; loop { if dir.ends_with("target") { return Ok(dir); @@ -81,3 +81,17 @@ fn target_dir() -> Result { } } } + +#[cfg(not(windows))] +fn canonicalize(path: impl AsRef) -> Result { + Ok(fs::canonicalize(path)?) +} + +#[cfg(windows)] +fn canonicalize(path: impl AsRef) -> Result { + // Real fs::canonicalize on Windows produces UNC paths which cl.exe is + // unable to handle in includes. Use a poor approximation instead. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + Ok(env::current_dir()?.join(path)) +} From c8a2494a0a10e236d053a18f5810594468763869 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 06:20:34 +0000 Subject: [PATCH 57/2232] Enable windows CI --- diff --git a/.travis.yml b/.travis.yml index 0248fc3..e3caff6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,5 +44,3 @@ matrix: script: - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress - allow_failures: - - os: windows # https://github.com/dtolnay/cxx/issues/25 From b3d52389a826c9fa0486459aa39993a6e24a1bfa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 06:20:34 +0000 Subject: [PATCH 58/2232] Use platform's path separator when writing cxxbridge.h --- diff --git a/src/lib.rs b/src/lib.rs index 6f40124..4c0f3c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -463,7 +463,7 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let mut build = paths::cc_build(); build.file(&bridge_path); - let ref cxxbridge_h = paths::include_dir()?.join("cxxbridge/cxxbridge.h"); + let ref cxxbridge_h = paths::include_dir()?.join("cxxbridge").join("cxxbridge.h"); let _ = fs::create_dir_all(cxxbridge_h.parent().unwrap()); let _ = fs::remove_file(cxxbridge_h); let _ = fs::write(cxxbridge_h, gen::include::HEADER); From 764d5e90bb6e6838fef20658548201f45d97b2bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 06:39:41 +0000 Subject: [PATCH 59/2232] Merge pull request #42 from dtolnay/windows Enable windows CI --- diff --git a/.travis.yml b/.travis.yml index 0248fc3..e3caff6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,5 +44,3 @@ matrix: script: - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress - allow_failures: - - os: windows # https://github.com/dtolnay/cxx/issues/25 From 3e7ac51fc068f7a0baf69c1c717bb0762ac322b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 07:18:12 +0000 Subject: [PATCH 60/2232] Add windows msvc build --- diff --git a/.travis.yml b/.travis.yml index e3caff6..aa158b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,14 +10,22 @@ script: matrix: include: - - os: macos - rust: nightly - - os: windows + - name: macOS + os: macos rust: nightly + - name: Windows (gnu) + os: windows + rust: nightly-x86_64-pc-windows-gnu before_script: # windows is bad at symlinks - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - name: Windows (msvc) + os: windows + rust: nightly-x86_64-pc-windows-msvc + before_script: + - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax + - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src - name: Buck rust: nightly before_install: From 089e479b228445b7b7a1db26e2355049039aee48 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 08:12:50 +0000 Subject: [PATCH 61/2232] Run buildifier to resort after hyphen conversion --- diff --git a/third-party/BUILD b/third-party/BUILD index 6792a98..e2f6d76 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -75,8 +75,8 @@ rust_library( srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ - ":proc-macro2", ":proc-macro-error-attr", + ":proc-macro2", ":quote", ":syn", ], @@ -167,8 +167,8 @@ rust_library( crate_type = "proc-macro", deps = [ ":heck", - ":proc-macro2", ":proc-macro-error", + ":proc-macro2", ":quote", ":syn", ], From 63576d458db8a10719c6b9c8a1f423624afa96e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 25 2020 08:13:25 +0000 Subject: [PATCH 62/2232] Move build -> tools/bazel Fixes https://github.com/dtolnay/cxx/issues/45. --- diff --git a/BUILD b/BUILD index 5d463b2..0405df1 100644 --- a/BUILD +++ b/BUILD @@ -1,4 +1,4 @@ -load("//:build/rust.bzl", "rust_binary", "rust_library") +load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", diff --git a/build/rust.bzl b/build/rust.bzl deleted file mode 100644 index 3ef4d91..0000000 --- a/build/rust.bzl +++ /dev/null @@ -1,23 +0,0 @@ -load( - "@io_bazel_rules_rust//rust:rust.bzl", - _rust_binary = "rust_binary", - _rust_library = "rust_library", - _rust_test = "rust_test", -) - -def rust_binary(edition = "2018", **kwargs): - _rust_binary(edition = edition, **kwargs) - -def third_party_rust_binary(rustc_flags = [], **kwargs): - rustc_flags = rustc_flags + ["--cap-lints=allow"] - rust_binary(rustc_flags = rustc_flags, **kwargs) - -def rust_library(edition = "2018", **kwargs): - _rust_library(edition = edition, **kwargs) - -def third_party_rust_library(rustc_flags = [], **kwargs): - rustc_flags = rustc_flags + ["--cap-lints=allow"] - rust_library(rustc_flags = rustc_flags, **kwargs) - -def rust_test(edition = "2018", **kwargs): - _rust_test(edition = edition, **kwargs) diff --git a/demo-rs/BUILD b/demo-rs/BUILD index 674b3d9..389ee58 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -1,4 +1,4 @@ -load("//:build/rust.bzl", "rust_binary", "rust_library") +load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_binary( name = "demo-rs", diff --git a/tests/BUILD b/tests/BUILD index a88cde5..16825f6 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,4 +1,4 @@ -load("//:build/rust.bzl", "rust_library", "rust_test") +load("//tools/bazel:rust.bzl", "rust_library", "rust_test") rust_test( name = "test", diff --git a/third-party/BUILD b/third-party/BUILD index e2f6d76..9af57d1 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -1,5 +1,5 @@ load( - "//:build/rust.bzl", + "//tools/bazel:rust.bzl", rust_binary = "third_party_rust_binary", rust_library = "third_party_rust_library", ) diff --git a/tools/bazel/BUILD b/tools/bazel/BUILD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/bazel/BUILD diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl new file mode 100644 index 0000000..3ef4d91 --- /dev/null +++ b/tools/bazel/rust.bzl @@ -0,0 +1,23 @@ +load( + "@io_bazel_rules_rust//rust:rust.bzl", + _rust_binary = "rust_binary", + _rust_library = "rust_library", + _rust_test = "rust_test", +) + +def rust_binary(edition = "2018", **kwargs): + _rust_binary(edition = edition, **kwargs) + +def third_party_rust_binary(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_binary(rustc_flags = rustc_flags, **kwargs) + +def rust_library(edition = "2018", **kwargs): + _rust_library(edition = edition, **kwargs) + +def third_party_rust_library(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_library(rustc_flags = rustc_flags, **kwargs) + +def rust_test(edition = "2018", **kwargs): + _rust_test(edition = edition, **kwargs) From 649337e12d79c2b275fb9b9e9266536844ffd1ae Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Feb 26 2020 02:11:20 +0000 Subject: [PATCH 63/2232] Move namespace alias to top of header for visibility --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 0bfe805..8061c28 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -4,6 +4,9 @@ #include #include +namespace cxxbridge01 {} +namespace cxxbridge = cxxbridge01; + namespace cxxbridge01 { class RustString final { @@ -128,5 +131,3 @@ std::ostream &operator<<(std::ostream &os, const RustString &s); std::ostream &operator<<(std::ostream &os, const RustStr &s); } // namespace cxxbridge01 - -namespace cxxbridge = cxxbridge01; From e976487440df123b535b01821381352ff31d8d7d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 20:49:21 +0000 Subject: [PATCH 64/2232] Update third-party deps to drop rustversion --- diff --git a/third-party/BUCK b/third-party/BUCK index 5c0e90d..0a5063f 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -67,7 +67,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.10/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -79,12 +79,11 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.10/src/**"]), proc_macro = True, deps = [ ":proc-macro2", ":quote", - ":rustversion", ":syn", ":syn-mid", ], @@ -92,7 +91,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.9/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -115,38 +114,8 @@ rust_library( ) rust_library( - name = "rustversion", - srcs = glob(["vendor/rustversion-1.0.2/src/**"]), - mapped_srcs = { - ":rustversion-buildscript-run": "vendor/rustversion-1.0.2/src/generated", - }, - proc_macro = True, - env = { - "OUT_DIR": "generated", - }, - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_binary( - name = "rustversion-buildscript", - srcs = glob(["vendor/rustversion-1.0.2/build/**"]), - crate_root = "vendor/rustversion-1.0.2/build/build.rs", -) - -genrule( - name = "rustversion-buildscript-run", - cmd = "OUT_DIR=${OUT} $(exe :rustversion-buildscript)", - type = "build.rs", - out = ".", -) - -rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.9/src/**"]), + srcs = glob(["vendor/structopt-0.3.11/src/**"]), visibility = ["PUBLIC"], deps = [ ":clap", @@ -157,7 +126,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.4/src/**"]), proc_macro = True, deps = [ ":heck", @@ -170,7 +139,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.15/src/**"]), + srcs = glob(["vendor/syn-1.0.16/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 9af57d1..697c125 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -72,7 +72,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.9/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.10/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -84,12 +84,11 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.9/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.10/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", ":quote", - ":rustversion", ":syn", ":syn-mid", ], @@ -97,7 +96,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.8/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.9/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -120,39 +119,8 @@ rust_library( ) rust_library( - name = "rustversion", - srcs = glob(["vendor/rustversion-1.0.2/src/**"]), - crate_type = "proc-macro", - out_dir_tar = ":rustversion_buildscript_outdir", - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_binary( - name = "rustversion_buildscript", - srcs = glob(["vendor/rustversion-1.0.2/build/**"]), - crate_root = "vendor/rustversion-1.0.2/build/build.rs", -) - -pkg_tar( - name = "rustversion_buildscript_outdir", - srcs = [":rustversion_buildscript_run"], - extension = "tar.gz", -) - -genrule( - name = "rustversion_buildscript_run", - outs = ["version.rs"], - cmd = "OUT_DIR=$(@D) $(location :rustversion_buildscript)", - tools = [":rustversion_buildscript"], -) - -rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.9/src/**"]), + srcs = glob(["vendor/structopt-0.3.11/src/**"]), visibility = ["//visibility:public"], deps = [ ":clap", @@ -163,7 +131,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.2/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.4/src/**"]), crate_type = "proc-macro", deps = [ ":heck", @@ -176,7 +144,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.15/src/**"]), + srcs = glob(["vendor/syn-1.0.16/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 5f2a1ca..4e6cc7d 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" +checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" dependencies = [ "libc", ] @@ -183,35 +183,35 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242" +checksum = "8a857f7c61b149c868eb7e40311b48502fcc924744fb73191962748643336568" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "rustversion", "syn", + "version_check", ] [[package]] name = "proc-macro-error-attr" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253" +checksum = "075d00534b62f176b55a48b68319be2f3fc05616d68ecd2bcb66bac0a49170e1" dependencies = [ "proc-macro2", "quote", - "rustversion", "syn", "syn-mid", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ "unicode-xid", ] @@ -281,9 +281,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bcbed7d48956fcbb5d80c6b95aedb553513de0a1b451ea92679d999c010e98" +checksum = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073" dependencies = [ "clap", "lazy_static", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095064aa1f5b94d14e635d0a5684cf140c43ae40a0fd990708d38f5d669e5f64" +checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" dependencies = [ "heck", "proc-macro-error", @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" dependencies = [ "proc-macro2", "quote", @@ -411,6 +411,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" + +[[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" From 7e219b8099c9372df5c564a506cbb83c1535d6b7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:14:55 +0000 Subject: [PATCH 65/2232] Use absolute qualified identifiers in generated C++ This avoids collision with the user's namespaces having the same name. Less important in the context of the current names right now, but more important after we move our public C++ API from cxxbridge:: to rust::. In fact our example code already uses org::rust:: as the namespace, inside of which a non-absolute rust:: would cause trouble. --- diff --git a/gen/write.rs b/gen/write.rs index ea6a812..9034a16 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -229,7 +229,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, &arg.ty); write!(out, "({})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { - write!(out, "std::move(*{})", arg.ident); + write!(out, "::std::move(*{})", arg.ident); } else { write!(out, "{}", arg.ident); } @@ -365,7 +365,7 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "cxxbridge::RustStr::Repr "), + Some(Type::Str(_)) => write!(out, "::cxxbridge::RustStr::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -377,7 +377,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "cxxbridge::RustStr::Repr "), + Type::Str(_) => write!(out, "::cxxbridge::RustStr::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -400,17 +400,17 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I32) => write!(out, "int32_t"), Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), - Some(CxxString) => write!(out, "std::string"), - Some(RustString) => write!(out, "cxxbridge::RustString"), + Some(CxxString) => write!(out, "::std::string"), + Some(RustString) => write!(out, "::cxxbridge::RustString"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { - write!(out, "cxxbridge::RustBox<"); + write!(out, "::cxxbridge::RustBox<"); write_type(out, &ty.inner); write!(out, ">"); } Type::UniquePtr(ptr) => { - write!(out, "std::unique_ptr<"); + write!(out, "::std::unique_ptr<"); write_type(out, &ptr.inner); write!(out, ">"); } @@ -422,7 +422,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write!(out, " &"); } Type::Str(_) => { - write!(out, "cxxbridge::RustStr"); + write!(out, "::cxxbridge::RustStr"); } } } @@ -482,27 +482,27 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$rust_box${}$uninit(cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$uninit(::cxxbridge::RustBox<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$rust_box${}$set_raw(cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$rust_box${}$set_raw(::cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge01$rust_box${}$drop(cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$drop(::cxxbridge::RustBox<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge01$rust_box${}$deref(const cxxbridge::RustBox<{}> *ptr) noexcept;", + "const {} *cxxbridge01$rust_box${}$deref(const ::cxxbridge::RustBox<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge01$rust_box${}$deref_mut(cxxbridge::RustBox<{}> *ptr) noexcept;", + "{} *cxxbridge01$rust_box${}$deref_mut(::cxxbridge::RustBox<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); @@ -588,56 +588,56 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_UNIQUE_PTR_{}", instance); writeln!( out, - "static_assert(sizeof(std::unique_ptr<{}>) == sizeof(void *), \"\");", + "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", inner, ); writeln!( out, - "static_assert(alignof(std::unique_ptr<{}>) == alignof(void *), \"\");", + "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", inner, ); writeln!( out, - "void cxxbridge01$unique_ptr${}$null(std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge01$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); - writeln!(out, " new (ptr) std::unique_ptr<{}>();", inner); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$new(std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge01$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( out, - " new (ptr) std::unique_ptr<{}>(new {}(std::move(*value)));", + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", inner, inner, ); writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$raw(std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge01$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); - writeln!(out, " new (ptr) std::unique_ptr<{}>(raw);", inner); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge01$unique_ptr${}$get(const std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge01$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge01$unique_ptr${}$release(std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge01$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$drop(std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge01$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); From b92e66f4dc0ad413509dfc43c16e95697e077fa2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:37:15 +0000 Subject: [PATCH 66/2232] Support nested blocks in C++ emitter --- diff --git a/gen/out.rs b/gen/out.rs index d6735be..8d7499b 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -5,8 +5,8 @@ pub(crate) struct OutFile { pub header: bool, content: Vec, section_pending: bool, - block: &'static str, - block_pending: bool, + blocks: Vec<&'static str>, + blocks_pending: usize, } impl OutFile { @@ -16,8 +16,8 @@ impl OutFile { header, content: Vec::new(), section_pending: false, - block: "", - block_pending: false, + blocks: Vec::new(), + blocks_pending: 0, } } @@ -27,18 +27,18 @@ impl OutFile { } pub fn begin_block(&mut self, block: &'static str) { - self.block = block; - self.block_pending = true; + self.blocks.push(block); + self.blocks_pending += 1; } pub fn end_block(&mut self) { - if self.block_pending { - self.block_pending = false; + if self.blocks_pending > 0 { + self.blocks_pending -= 1; } else { self.content.extend_from_slice(b"} // "); - self.content.extend_from_slice(self.block.as_bytes()); + self.content + .extend_from_slice(self.blocks.pop().unwrap().as_bytes()); self.content.push(b'\n'); - self.block = ""; self.section_pending = true; } } @@ -51,11 +51,13 @@ impl OutFile { impl Write for OutFile { fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { - if self.block_pending { + if self.blocks_pending > 0 { self.content.push(b'\n'); - self.content.extend_from_slice(self.block.as_bytes()); - self.content.extend_from_slice(b" {\n"); - self.block_pending = false; + for block in &self.blocks[self.blocks.len() - self.blocks_pending..] { + self.content.extend_from_slice(block.as_bytes()); + self.content.extend_from_slice(b" {\n"); + } + self.blocks_pending = 0; self.section_pending = false; } else if self.section_pending { self.content.push(b'\n'); From d944446b9898a120b2f66a54ca86240e7189af9e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:19 +0000 Subject: [PATCH 67/2232] Merge pull request #47 from dtolnay/nested Support nested blocks in C++ emitter --- diff --git a/gen/out.rs b/gen/out.rs index d6735be..8d7499b 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -5,8 +5,8 @@ pub(crate) struct OutFile { pub header: bool, content: Vec, section_pending: bool, - block: &'static str, - block_pending: bool, + blocks: Vec<&'static str>, + blocks_pending: usize, } impl OutFile { @@ -16,8 +16,8 @@ impl OutFile { header, content: Vec::new(), section_pending: false, - block: "", - block_pending: false, + blocks: Vec::new(), + blocks_pending: 0, } } @@ -27,18 +27,18 @@ impl OutFile { } pub fn begin_block(&mut self, block: &'static str) { - self.block = block; - self.block_pending = true; + self.blocks.push(block); + self.blocks_pending += 1; } pub fn end_block(&mut self) { - if self.block_pending { - self.block_pending = false; + if self.blocks_pending > 0 { + self.blocks_pending -= 1; } else { self.content.extend_from_slice(b"} // "); - self.content.extend_from_slice(self.block.as_bytes()); + self.content + .extend_from_slice(self.blocks.pop().unwrap().as_bytes()); self.content.push(b'\n'); - self.block = ""; self.section_pending = true; } } @@ -51,11 +51,13 @@ impl OutFile { impl Write for OutFile { fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { - if self.block_pending { + if self.blocks_pending > 0 { self.content.push(b'\n'); - self.content.extend_from_slice(self.block.as_bytes()); - self.content.extend_from_slice(b" {\n"); - self.block_pending = false; + for block in &self.blocks[self.blocks.len() - self.blocks_pending..] { + self.content.extend_from_slice(block.as_bytes()); + self.content.extend_from_slice(b" {\n"); + } + self.blocks_pending = 0; self.section_pending = false; } else if self.section_pending { self.content.push(b'\n'); From 560821697943b62ccce25c8f75b226e355ad6835 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:28 +0000 Subject: [PATCH 68/2232] Rename C++ RustString to String --- diff --git a/README.md b/README.md index e7eaa3a..406518b 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,7 @@ of functions. - + diff --git a/gen/write.rs b/gen/write.rs index 9034a16..d55d4ac 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -401,7 +401,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::cxxbridge::RustString"), + Some(RustString) => write!(out, "::cxxbridge::String"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 8061c28..395ec18 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -9,16 +9,16 @@ namespace cxxbridge = cxxbridge01; namespace cxxbridge01 { -class RustString final { +class String final { public: - RustString() noexcept; - RustString(const RustString &other) noexcept; - RustString(RustString &&other) noexcept; - RustString(const char *s); - RustString(const std::string &s); - RustString &operator=(const RustString &other) noexcept; - RustString &operator=(RustString &&other) noexcept; - ~RustString() noexcept; + String() noexcept; + String(const String &other) noexcept; + String(String &&other) noexcept; + String(const char *s); + String(const std::string &s); + String &operator=(const String &other) noexcept; + String &operator=(String &&other) noexcept; + ~String() noexcept; operator std::string() const; // Note: no null terminator. @@ -127,7 +127,7 @@ private: }; #endif // CXXBRIDGE01_RUST_BOX -std::ostream &operator<<(std::ostream &os, const RustString &s); +std::ostream &operator<<(std::ostream &os, const String &s); std::ostream &operator<<(std::ostream &os, const RustStr &s); } // namespace cxxbridge01 diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index a502d20..1134c6b 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -14,16 +14,16 @@ size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { return s.length(); } -// RustString -void cxxbridge01$rust_string$new(cxxbridge::RustString *self) noexcept; -void cxxbridge01$rust_string$clone(cxxbridge::RustString *self, - const cxxbridge::RustString &other) noexcept; -bool cxxbridge01$rust_string$from(cxxbridge::RustString *self, const char *ptr, +// cxxbridge::String +void cxxbridge01$rust_string$new(cxxbridge::String *self) noexcept; +void cxxbridge01$rust_string$clone(cxxbridge::String *self, + const cxxbridge::String &other) noexcept; +bool cxxbridge01$rust_string$from(cxxbridge::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge01$rust_string$drop(cxxbridge::RustString *self) noexcept; +void cxxbridge01$rust_string$drop(cxxbridge::String *self) noexcept; const char * -cxxbridge01$rust_string$ptr(const cxxbridge::RustString *self) noexcept; -size_t cxxbridge01$rust_string$len(const cxxbridge::RustString *self) noexcept; +cxxbridge01$rust_string$ptr(const cxxbridge::String *self) noexcept; +size_t cxxbridge01$rust_string$len(const cxxbridge::String *self) noexcept; // RustStr bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; @@ -31,39 +31,39 @@ bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; namespace cxxbridge01 { -RustString::RustString() noexcept { cxxbridge01$rust_string$new(this); } +String::String() noexcept { cxxbridge01$rust_string$new(this); } -RustString::RustString(const RustString &other) noexcept { +String::String(const String &other) noexcept { cxxbridge01$rust_string$clone(this, other); } -RustString::RustString(RustString &&other) noexcept { +String::String(String &&other) noexcept { this->repr = other.repr; cxxbridge01$rust_string$new(&other); } -RustString::RustString(const char *s) { +String::String(const char *s) { auto len = strlen(s); if (!cxxbridge01$rust_string$from(this, s, len)) { - throw std::invalid_argument("data for RustString is not utf-8"); + throw std::invalid_argument("data for cxxbridge::String is not utf-8"); } } -RustString::RustString(const std::string &s) { +String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); if (!cxxbridge01$rust_string$from(this, ptr, len)) { - throw std::invalid_argument("data for RustString is not utf-8"); + throw std::invalid_argument("data for cxxbridge::String is not utf-8"); } } -RustString::~RustString() noexcept { cxxbridge01$rust_string$drop(this); } +String::~String() noexcept { cxxbridge01$rust_string$drop(this); } -RustString::operator std::string() const { +String::operator std::string() const { return std::string(this->data(), this->size()); } -RustString &RustString::operator=(const RustString &other) noexcept { +String &String::operator=(const String &other) noexcept { if (this != &other) { cxxbridge01$rust_string$drop(this); cxxbridge01$rust_string$clone(this, other); @@ -71,7 +71,7 @@ RustString &RustString::operator=(const RustString &other) noexcept { return *this; } -RustString &RustString::operator=(RustString &&other) noexcept { +String &String::operator=(String &&other) noexcept { if (this != &other) { cxxbridge01$rust_string$drop(this); this->repr = other.repr; @@ -80,19 +80,19 @@ RustString &RustString::operator=(RustString &&other) noexcept { return *this; } -const char *RustString::data() const noexcept { +const char *String::data() const noexcept { return cxxbridge01$rust_string$ptr(this); } -size_t RustString::size() const noexcept { +size_t String::size() const noexcept { return cxxbridge01$rust_string$len(this); } -size_t RustString::length() const noexcept { +size_t String::length() const noexcept { return cxxbridge01$rust_string$len(this); } -std::ostream &operator<<(std::ostream &os, const RustString &s) { +std::ostream &operator<<(std::ostream &os, const String &s) { os.write(s.data(), s.size()); return os; } diff --git a/src/lib.rs b/src/lib.rs index 4c0f3c9..69f9447 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,7 +302,7 @@ //! //!
name in Rustname in C++restrictions
Stringcxxbridge::RustString
Stringcxxbridge::String
&strcxxbridge::RustStr
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
//! -//! +//! //! //! //! diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d27df41..60f6141 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -24,7 +24,7 @@ cxxbridge::RustStr c_return_str(const Shared &shared) { return "2020"; } -cxxbridge::RustString c_return_rust_string() { return "2020"; } +cxxbridge::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); @@ -67,7 +67,7 @@ void c_take_str(cxxbridge::RustStr s) { } } -void c_take_rust_string(cxxbridge::RustString s) { +void c_take_rust_string(cxxbridge::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -100,7 +100,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(cxxbridge::RustStr("2020")); - // TODO r_take_rust_string(cxxbridge::RustString("2020")); + // TODO r_take_rust_string(cxxbridge::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 87aac67..fa4e3c8 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,7 +23,7 @@ cxxbridge::RustBox c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); cxxbridge::RustStr c_return_str(const Shared &shared); -cxxbridge::RustString c_return_rust_string(); +cxxbridge::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); @@ -33,7 +33,7 @@ void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); void c_take_str(cxxbridge::RustStr s); -void c_take_rust_string(cxxbridge::RustString s); +void c_take_rust_string(cxxbridge::String s); void c_take_unique_ptr_string(std::unique_ptr s); } // namespace tests From 09dbe75a5dac7bde0d18ccf659d898d6b5007c61 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:28 +0000 Subject: [PATCH 69/2232] Rename C++ RustStr to Str --- diff --git a/README.md b/README.md index 406518b..551bc7c 100644 --- a/README.md +++ b/README.md @@ -299,7 +299,7 @@ of functions.
name in Rustname in C++restrictions
Stringcxxbridge::RustString
Stringcxxbridge::String
&strcxxbridge::RustStr
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
- + diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 500ee3a..3900572 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,7 +9,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(cxxbridge::RustStr appname) { +std::unique_ptr make_demo(cxxbridge::Str appname) { return std::unique_ptr(new ThingC(appname)); } diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 417edfb..d41ea7a 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -16,7 +16,7 @@ public: struct SharedThing; -std::unique_ptr make_demo(cxxbridge::RustStr appname); +std::unique_ptr make_demo(cxxbridge::Str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); diff --git a/gen/write.rs b/gen/write.rs index d55d4ac..063a674 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -365,7 +365,7 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::cxxbridge::RustStr::Repr "), + Some(Type::Str(_)) => write!(out, "::cxxbridge::Str::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -377,7 +377,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::cxxbridge::RustStr::Repr "), + Type::Str(_) => write!(out, "::cxxbridge::Str::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -422,7 +422,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write!(out, " &"); } Type::Str(_) => { - write!(out, "::cxxbridge::RustStr"); + write!(out, "::cxxbridge::Str"); } } } diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 395ec18..fb00bb4 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -31,14 +31,14 @@ private: std::array repr; }; -class RustStr final { +class Str final { public: - RustStr() noexcept; - RustStr(const char *s); - RustStr(const std::string &s); - RustStr(std::string &&s) = delete; - RustStr(const RustStr &other) noexcept; - RustStr &operator=(RustStr other) noexcept; + Str() noexcept; + Str(const char *s); + Str(const std::string &s); + Str(std::string &&s) = delete; + Str(const Str &other) noexcept; + Str &operator=(Str other) noexcept; operator std::string() const; // Note: no null terminator. @@ -54,7 +54,7 @@ public: const char *ptr; size_t len; }; - RustStr(Repr repr) noexcept; + Str(Repr repr) noexcept; operator Repr() noexcept; private: @@ -128,6 +128,6 @@ private: #endif // CXXBRIDGE01_RUST_BOX std::ostream &operator<<(std::ostream &os, const String &s); -std::ostream &operator<<(std::ostream &os, const RustStr &s); +std::ostream &operator<<(std::ostream &os, const Str &s); } // namespace cxxbridge01 diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 1134c6b..da0b450 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -25,7 +25,7 @@ const char * cxxbridge01$rust_string$ptr(const cxxbridge::String *self) noexcept; size_t cxxbridge01$rust_string$len(const cxxbridge::String *self) noexcept; -// RustStr +// cxxbridge::Str bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; } // extern "C" @@ -97,43 +97,43 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -RustStr::RustStr() noexcept +Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} -RustStr::RustStr(const char *s) : repr(Repr{s, strlen(s)}) { +Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for RustStr is not utf-8"); + throw std::invalid_argument("data for cxxbridge::Str is not utf-8"); } } -RustStr::RustStr(const std::string &s) : repr(Repr{s.data(), s.length()}) { +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for RustStr is not utf-8"); + throw std::invalid_argument("data for cxxbridge::Str is not utf-8"); } } -RustStr::RustStr(const RustStr &) noexcept = default; +Str::Str(const Str &) noexcept = default; -RustStr &RustStr::operator=(RustStr other) noexcept { +Str &Str::operator=(Str other) noexcept { this->repr = other.repr; return *this; } -RustStr::operator std::string() const { +Str::operator std::string() const { return std::string(this->data(), this->size()); } -const char *RustStr::data() const noexcept { return this->repr.ptr; } +const char *Str::data() const noexcept { return this->repr.ptr; } -size_t RustStr::size() const noexcept { return this->repr.len; } +size_t Str::size() const noexcept { return this->repr.len; } -size_t RustStr::length() const noexcept { return this->repr.len; } +size_t Str::length() const noexcept { return this->repr.len; } -RustStr::RustStr(Repr repr_) noexcept : repr(repr_) {} +Str::Str(Repr repr_) noexcept : repr(repr_) {} -RustStr::operator Repr() noexcept { return this->repr; } +Str::operator Repr() noexcept { return this->repr; } -std::ostream &operator<<(std::ostream &os, const RustStr &s) { +std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); return os; } diff --git a/src/lib.rs b/src/lib.rs index 69f9447..2f21166 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,7 +303,7 @@ //!
name in Rustname in C++restrictions
Stringcxxbridge::String
&strcxxbridge::RustStr
&strcxxbridge::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
//! //! -//! +//! //! //! //! diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 60f6141..a5305ef 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -19,7 +19,7 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } -cxxbridge::RustStr c_return_str(const Shared &shared) { +cxxbridge::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; } @@ -61,7 +61,7 @@ void c_take_ref_c(const C &c) { } } -void c_take_str(cxxbridge::RustStr s) { +void c_take_str(cxxbridge::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -99,7 +99,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); - r_take_str(cxxbridge::RustStr("2020")); + r_take_str(cxxbridge::Str("2020")); // TODO r_take_rust_string(cxxbridge::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index fa4e3c8..fe3d966 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -22,7 +22,7 @@ Shared c_return_shared(); cxxbridge::RustBox c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); -cxxbridge::RustStr c_return_str(const Shared &shared); +cxxbridge::Str c_return_str(const Shared &shared); cxxbridge::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); @@ -32,7 +32,7 @@ void c_take_box(cxxbridge::RustBox r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); -void c_take_str(cxxbridge::RustStr s); +void c_take_str(cxxbridge::Str s); void c_take_rust_string(cxxbridge::String s); void c_take_unique_ptr_string(std::unique_ptr s); From 324437a263a6b32149abf7133e96d437865a1fec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:29 +0000 Subject: [PATCH 70/2232] Rename C++ RustBox to Box --- diff --git a/README.md b/README.md index 551bc7c..27c0c83 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,7 @@ of functions. - +
name in Rustname in C++restrictions
Stringcxxbridge::String
&strcxxbridge::RustStr
&strcxxbridge::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Stringcxxbridge::String
&strcxxbridge::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
Box<T>cxxbridge::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
diff --git a/gen/write.rs b/gen/write.rs index 063a674..31eee36 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -405,7 +405,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { None => write!(out, "{}", ident), }, Type::RustBox(ty) => { - write!(out, "::cxxbridge::RustBox<"); + write!(out, "::cxxbridge::Box<"); write_type(out, &ty.inner); write!(out, ">"); } @@ -482,27 +482,27 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$rust_box${}$uninit(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$uninit(::cxxbridge::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$rust_box${}$set_raw(::cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$rust_box${}$set_raw(::cxxbridge::Box<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge01$rust_box${}$drop(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$drop(::cxxbridge::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge01$rust_box${}$deref(const ::cxxbridge::RustBox<{}> *ptr) noexcept;", + "const {} *cxxbridge01$rust_box${}$deref(const ::cxxbridge::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge01$rust_box${}$deref_mut(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "{} *cxxbridge01$rust_box${}$deref_mut(::cxxbridge::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); @@ -518,7 +518,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { let instance = inner.replace("::", "$"); writeln!(out, "template <>"); - writeln!(out, "void RustBox<{}>::uninit() noexcept {{", inner); + writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); writeln!( out, " return cxxbridge01$rust_box${}$uninit(this);", @@ -529,7 +529,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!( out, - "void RustBox<{}>::set_raw({} *raw) noexcept {{", + "void Box<{}>::set_raw({} *raw) noexcept {{", inner, inner, ); writeln!( @@ -540,7 +540,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!(out, "template <>"); - writeln!(out, "void RustBox<{}>::drop() noexcept {{", inner); + writeln!(out, "void Box<{}>::drop() noexcept {{", inner); writeln!( out, " return cxxbridge01$rust_box${}$drop(this);", @@ -551,7 +551,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!( out, - "const {} *RustBox<{}>::deref() const noexcept {{", + "const {} *Box<{}>::deref() const noexcept {{", inner, inner, ); writeln!( @@ -562,11 +562,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!(out, "template <>"); - writeln!( - out, - "{} *RustBox<{}>::deref_mut() noexcept {{", - inner, inner, - ); + writeln!(out, "{} *Box<{}>::deref_mut() noexcept {{", inner, inner); writeln!( out, " return cxxbridge01$rust_box${}$deref_mut(this);", diff --git a/include/cxxbridge.h b/include/cxxbridge.h index fb00bb4..2ff3d20 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -63,15 +63,15 @@ private: #ifndef CXXBRIDGE01_RUST_BOX #define CXXBRIDGE01_RUST_BOX -template class RustBox final { +template class Box final { public: - RustBox(const RustBox &other) : RustBox(*other) {} - RustBox(RustBox &&other) noexcept : repr(other.repr) { other.repr = 0; } - RustBox(const T &val) { + Box(const Box &other) : Box(*other) {} + Box(Box &&other) noexcept : repr(other.repr) { other.repr = 0; } + Box(const T &val) { this->uninit(); ::new (this->deref_mut()) T(val); } - RustBox &operator=(const RustBox &other) { + Box &operator=(const Box &other) { if (this != &other) { if (this->repr) { **this = *other; @@ -82,7 +82,7 @@ public: } return *this; } - RustBox &operator=(RustBox &&other) noexcept { + Box &operator=(Box &&other) noexcept { if (this->repr) { this->drop(); } @@ -90,7 +90,7 @@ public: other.repr = 0; return *this; } - ~RustBox() noexcept { + ~Box() noexcept { if (this->repr) { this->drop(); } @@ -103,8 +103,8 @@ public: // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. - static RustBox from_raw(T *raw) noexcept { - RustBox box; + static Box from_raw(T *raw) noexcept { + Box box; box.set_raw(raw); return box; } @@ -116,7 +116,7 @@ public: } private: - RustBox() noexcept {} + Box() noexcept {} void uninit() noexcept; void set_raw(T *) noexcept; T *get_raw() noexcept; diff --git a/src/lib.rs b/src/lib.rs index 2f21166..abf7321 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -305,7 +305,7 @@ //! Stringcxxbridge::String //! &strcxxbridge::Str //! CxxStringstd::stringcannot be passed by value -//! Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type +//! Box<T>cxxbridge::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type //! //! diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index a5305ef..5800f29 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -42,7 +42,7 @@ void c_take_shared(Shared shared) { } } -void c_take_box(cxxbridge::RustBox r) { +void c_take_box(cxxbridge::Box r) { (void)r; cxx_test_suite_set_correct(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index fe3d966..3600eb6 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,7 +19,7 @@ private: size_t c_return_primitive(); Shared c_return_shared(); -cxxbridge::RustBox c_return_box(); +cxxbridge::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); cxxbridge::Str c_return_str(const Shared &shared); @@ -28,7 +28,7 @@ std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); -void c_take_box(cxxbridge::RustBox r); +void c_take_box(cxxbridge::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); From 750755e55771dca2674a6bd5a2a5a59b86f0d8f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:29 +0000 Subject: [PATCH 71/2232] Rename namespace to rust::inline cxxbridge01 --- diff --git a/README.md b/README.md index 27c0c83..07ea91e 100644 --- a/README.md +++ b/README.md @@ -298,10 +298,10 @@ of functions. - - + + - +
name in Rustname in C++restrictions
Stringcxxbridge::String
&strcxxbridge::Str
Stringrust::String
&strrust::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::Box<T>cannot hold opaque C++ type
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 3900572..79340eb 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,7 +9,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(cxxbridge::Str appname) { +std::unique_ptr make_demo(::rust::Str appname) { return std::unique_ptr(new ThingC(appname)); } diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index d41ea7a..aa417c7 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -16,7 +16,7 @@ public: struct SharedThing; -std::unique_ptr make_demo(cxxbridge::Str appname); +std::unique_ptr make_demo(::rust::Str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); diff --git a/gen/write.rs b/gen/write.rs index 31eee36..a2c1b7a 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -21,21 +21,11 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b write_includes(out, types); write_include_cxxbridge(out, types); - if !header { - out.next_section(); - write_namespace_alias(out, types); - } - out.next_section(); for name in &namespace { writeln!(out, "namespace {} {{", name); } - if header { - out.next_section(); - write_namespace_alias(out, types); - } - out.next_section(); for api in apis { match api { @@ -125,7 +115,8 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } - out.begin_block("namespace cxxbridge01"); + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge01"); if needs_rust_box { writeln!(out, "// #include \"cxxbridge.h\""); for line in include::get("CXXBRIDGE01_RUST_BOX").lines() { @@ -135,20 +126,7 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } out.end_block(); -} - -fn write_namespace_alias(out: &mut OutFile, types: &Types) { - let mut needs_namespace_alias = false; - for ty in types { - if let Type::RustBox(_) = ty { - needs_namespace_alias = true; - break; - } - } - - if needs_namespace_alias { - writeln!(out, "namespace cxxbridge = cxxbridge01;"); - } + out.end_block(); } fn write_struct(out: &mut OutFile, strct: &Struct) { @@ -365,7 +343,7 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::cxxbridge::Str::Repr "), + Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -377,7 +355,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::cxxbridge::Str::Repr "), + Type::Str(_) => write!(out, "::rust::Str::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -401,11 +379,11 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::cxxbridge::String"), + Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { - write!(out, "::cxxbridge::Box<"); + write!(out, "::rust::Box<"); write_type(out, &ty.inner); write!(out, ">"); } @@ -422,7 +400,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write!(out, " &"); } Type::Str(_) => { - write!(out, "::cxxbridge::Str"); + write!(out, "::rust::Str"); } } } @@ -458,7 +436,8 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } out.end_block(); - out.begin_block("namespace cxxbridge01"); + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge01"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -467,6 +446,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } out.end_block(); + out.end_block(); } fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { @@ -482,27 +462,27 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$rust_box${}$uninit(::cxxbridge::Box<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$rust_box${}$set_raw(::cxxbridge::Box<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$rust_box${}$set_raw(::rust::Box<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge01$rust_box${}$drop(::cxxbridge::Box<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge01$rust_box${}$deref(const ::cxxbridge::Box<{}> *ptr) noexcept;", + "const {} *cxxbridge01$rust_box${}$deref(const ::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge01$rust_box${}$deref_mut(::cxxbridge::Box<{}> *ptr) noexcept;", + "{} *cxxbridge01$rust_box${}$deref_mut(::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 2ff3d20..a668a66 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -4,10 +4,8 @@ #include #include -namespace cxxbridge01 {} -namespace cxxbridge = cxxbridge01; - -namespace cxxbridge01 { +namespace rust { +inline namespace cxxbridge01 { class String final { public: @@ -130,4 +128,5 @@ private: std::ostream &operator<<(std::ostream &os, const String &s); std::ostream &operator<<(std::ostream &os, const Str &s); -} // namespace cxxbridge01 +} // inline namespace cxxbridge01 +} // namespace rust diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index da0b450..c139688 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -3,8 +3,6 @@ #include #include -namespace cxxbridge = cxxbridge01; - extern "C" { const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -14,22 +12,23 @@ size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { return s.length(); } -// cxxbridge::String -void cxxbridge01$rust_string$new(cxxbridge::String *self) noexcept; -void cxxbridge01$rust_string$clone(cxxbridge::String *self, - const cxxbridge::String &other) noexcept; -bool cxxbridge01$rust_string$from(cxxbridge::String *self, const char *ptr, +// rust::String +void cxxbridge01$rust_string$new(rust::String *self) noexcept; +void cxxbridge01$rust_string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge01$rust_string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge01$rust_string$drop(cxxbridge::String *self) noexcept; +void cxxbridge01$rust_string$drop(rust::String *self) noexcept; const char * -cxxbridge01$rust_string$ptr(const cxxbridge::String *self) noexcept; -size_t cxxbridge01$rust_string$len(const cxxbridge::String *self) noexcept; +cxxbridge01$rust_string$ptr(const rust::String *self) noexcept; +size_t cxxbridge01$rust_string$len(const rust::String *self) noexcept; -// cxxbridge::Str +// rust::Str bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; } // extern "C" -namespace cxxbridge01 { +namespace rust { +inline namespace cxxbridge01 { String::String() noexcept { cxxbridge01$rust_string$new(this); } @@ -45,7 +44,7 @@ String::String(String &&other) noexcept { String::String(const char *s) { auto len = strlen(s); if (!cxxbridge01$rust_string$from(this, s, len)) { - throw std::invalid_argument("data for cxxbridge::String is not utf-8"); + throw std::invalid_argument("data for rust::String is not utf-8"); } } @@ -53,7 +52,7 @@ String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); if (!cxxbridge01$rust_string$from(this, ptr, len)) { - throw std::invalid_argument("data for cxxbridge::String is not utf-8"); + throw std::invalid_argument("data for rust::String is not utf-8"); } } @@ -102,13 +101,13 @@ Str::Str() noexcept Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for cxxbridge::Str is not utf-8"); + throw std::invalid_argument("data for rust::Str is not utf-8"); } } Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for cxxbridge::Str is not utf-8"); + throw std::invalid_argument("data for rust::Str is not utf-8"); } } @@ -138,7 +137,8 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } -} // namespace cxxbridge01 +} // inline namespace cxxbridge01 +} // namespace rust extern "C" { void cxxbridge01$unique_ptr$std$string$null( diff --git a/src/lib.rs b/src/lib.rs index abf7321..006159a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,10 +302,10 @@ //! //! //! -//! -//! +//! +//! //! -//! +//! //! //! //!
name in Rustname in C++restrictions
Stringcxxbridge::String
&strcxxbridge::Str
Stringrust::String
&strrust::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::Box<T>cannot hold opaque C++ type
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 5800f29..65640df 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -19,12 +19,12 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } -cxxbridge::Str c_return_str(const Shared &shared) { +rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; } -cxxbridge::String c_return_rust_string() { return "2020"; } +rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); @@ -42,7 +42,7 @@ void c_take_shared(Shared shared) { } } -void c_take_box(cxxbridge::Box r) { +void c_take_box(rust::Box r) { (void)r; cxx_test_suite_set_correct(); } @@ -61,13 +61,13 @@ void c_take_ref_c(const C &c) { } } -void c_take_str(cxxbridge::Str s) { +void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } } -void c_take_rust_string(cxxbridge::String s) { +void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -99,8 +99,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); - r_take_str(cxxbridge::Str("2020")); - // TODO r_take_rust_string(cxxbridge::String("2020")); + r_take_str(rust::Str("2020")); + // TODO r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 3600eb6..f41cc5e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,21 +19,21 @@ private: size_t c_return_primitive(); Shared c_return_shared(); -cxxbridge::Box c_return_box(); +rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); -cxxbridge::Str c_return_str(const Shared &shared); -cxxbridge::String c_return_rust_string(); +rust::Str c_return_str(const Shared &shared); +rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); -void c_take_box(cxxbridge::Box r); +void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); -void c_take_str(cxxbridge::Str s); -void c_take_rust_string(cxxbridge::String s); +void c_take_str(rust::Str s); +void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); } // namespace tests From aa17a5acbb8c3bdfdbfa5f2ba6cdcb03718ffcf4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 21:47:29 +0000 Subject: [PATCH 72/2232] Rename example's namespace to org::example --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 79340eb..b975e88 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -3,13 +3,13 @@ #include namespace org { -namespace rust { +namespace example { ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(::rust::Str appname) { +std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(appname)); } @@ -17,5 +17,5 @@ const std::string &get_name(const ThingC &thing) { return thing.appname; } void do_thing(SharedThing state) { print_r(*state.y); } -} // namespace rust +} // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index aa417c7..a579986 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -4,7 +4,7 @@ #include namespace org { -namespace rust { +namespace example { class ThingC { public: @@ -16,9 +16,9 @@ public: struct SharedThing; -std::unique_ptr make_demo(::rust::Str appname); +std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); -} // namespace rust +} // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index dba4727..66dfc79 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -1,4 +1,4 @@ -#[cxx::bridge(namespace = org::rust)] +#[cxx::bridge(namespace = org::example)] mod ffi { struct SharedThing { z: i32, From e3bd6abf480343b3071e1ada61b9fc50cbf84913 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:00:40 +0000 Subject: [PATCH 73/2232] Merge pull request #48 from dtolnay/namespace Better use of namespaces --- diff --git a/README.md b/README.md index e7eaa3a..07ea91e 100644 --- a/README.md +++ b/README.md @@ -298,10 +298,10 @@ of functions. - - + + - +
name in Rustname in C++restrictions
Stringcxxbridge::RustString
&strcxxbridge::RustStr
Stringrust::String
&strrust::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 500ee3a..b975e88 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -3,13 +3,13 @@ #include namespace org { -namespace rust { +namespace example { ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(cxxbridge::RustStr appname) { +std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(appname)); } @@ -17,5 +17,5 @@ const std::string &get_name(const ThingC &thing) { return thing.appname; } void do_thing(SharedThing state) { print_r(*state.y); } -} // namespace rust +} // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 417edfb..a579986 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -4,7 +4,7 @@ #include namespace org { -namespace rust { +namespace example { class ThingC { public: @@ -16,9 +16,9 @@ public: struct SharedThing; -std::unique_ptr make_demo(cxxbridge::RustStr appname); +std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); -} // namespace rust +} // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index dba4727..66dfc79 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -1,4 +1,4 @@ -#[cxx::bridge(namespace = org::rust)] +#[cxx::bridge(namespace = org::example)] mod ffi { struct SharedThing { z: i32, diff --git a/gen/write.rs b/gen/write.rs index 9034a16..a2c1b7a 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -21,21 +21,11 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b write_includes(out, types); write_include_cxxbridge(out, types); - if !header { - out.next_section(); - write_namespace_alias(out, types); - } - out.next_section(); for name in &namespace { writeln!(out, "namespace {} {{", name); } - if header { - out.next_section(); - write_namespace_alias(out, types); - } - out.next_section(); for api in apis { match api { @@ -125,7 +115,8 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } - out.begin_block("namespace cxxbridge01"); + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge01"); if needs_rust_box { writeln!(out, "// #include \"cxxbridge.h\""); for line in include::get("CXXBRIDGE01_RUST_BOX").lines() { @@ -135,20 +126,7 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } out.end_block(); -} - -fn write_namespace_alias(out: &mut OutFile, types: &Types) { - let mut needs_namespace_alias = false; - for ty in types { - if let Type::RustBox(_) = ty { - needs_namespace_alias = true; - break; - } - } - - if needs_namespace_alias { - writeln!(out, "namespace cxxbridge = cxxbridge01;"); - } + out.end_block(); } fn write_struct(out: &mut OutFile, strct: &Struct) { @@ -365,7 +343,7 @@ fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::cxxbridge::RustStr::Repr "), + Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -377,7 +355,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::cxxbridge::RustStr::Repr "), + Type::Str(_) => write!(out, "::rust::Str::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -401,11 +379,11 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::cxxbridge::RustString"), + Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { - write!(out, "::cxxbridge::RustBox<"); + write!(out, "::rust::Box<"); write_type(out, &ty.inner); write!(out, ">"); } @@ -422,7 +400,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write!(out, " &"); } Type::Str(_) => { - write!(out, "::cxxbridge::RustStr"); + write!(out, "::rust::Str"); } } } @@ -458,7 +436,8 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } out.end_block(); - out.begin_block("namespace cxxbridge01"); + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge01"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -467,6 +446,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } out.end_block(); + out.end_block(); } fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { @@ -482,27 +462,27 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$rust_box${}$uninit(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$rust_box${}$set_raw(::cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$rust_box${}$set_raw(::rust::Box<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge01$rust_box${}$drop(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "void cxxbridge01$rust_box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge01$rust_box${}$deref(const ::cxxbridge::RustBox<{}> *ptr) noexcept;", + "const {} *cxxbridge01$rust_box${}$deref(const ::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge01$rust_box${}$deref_mut(::cxxbridge::RustBox<{}> *ptr) noexcept;", + "{} *cxxbridge01$rust_box${}$deref_mut(::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); @@ -518,7 +498,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { let instance = inner.replace("::", "$"); writeln!(out, "template <>"); - writeln!(out, "void RustBox<{}>::uninit() noexcept {{", inner); + writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); writeln!( out, " return cxxbridge01$rust_box${}$uninit(this);", @@ -529,7 +509,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!( out, - "void RustBox<{}>::set_raw({} *raw) noexcept {{", + "void Box<{}>::set_raw({} *raw) noexcept {{", inner, inner, ); writeln!( @@ -540,7 +520,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!(out, "template <>"); - writeln!(out, "void RustBox<{}>::drop() noexcept {{", inner); + writeln!(out, "void Box<{}>::drop() noexcept {{", inner); writeln!( out, " return cxxbridge01$rust_box${}$drop(this);", @@ -551,7 +531,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!( out, - "const {} *RustBox<{}>::deref() const noexcept {{", + "const {} *Box<{}>::deref() const noexcept {{", inner, inner, ); writeln!( @@ -562,11 +542,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!(out, "template <>"); - writeln!( - out, - "{} *RustBox<{}>::deref_mut() noexcept {{", - inner, inner, - ); + writeln!(out, "{} *Box<{}>::deref_mut() noexcept {{", inner, inner); writeln!( out, " return cxxbridge01$rust_box${}$deref_mut(this);", diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 8061c28..a668a66 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -4,21 +4,19 @@ #include #include -namespace cxxbridge01 {} -namespace cxxbridge = cxxbridge01; +namespace rust { +inline namespace cxxbridge01 { -namespace cxxbridge01 { - -class RustString final { +class String final { public: - RustString() noexcept; - RustString(const RustString &other) noexcept; - RustString(RustString &&other) noexcept; - RustString(const char *s); - RustString(const std::string &s); - RustString &operator=(const RustString &other) noexcept; - RustString &operator=(RustString &&other) noexcept; - ~RustString() noexcept; + String() noexcept; + String(const String &other) noexcept; + String(String &&other) noexcept; + String(const char *s); + String(const std::string &s); + String &operator=(const String &other) noexcept; + String &operator=(String &&other) noexcept; + ~String() noexcept; operator std::string() const; // Note: no null terminator. @@ -31,14 +29,14 @@ private: std::array repr; }; -class RustStr final { +class Str final { public: - RustStr() noexcept; - RustStr(const char *s); - RustStr(const std::string &s); - RustStr(std::string &&s) = delete; - RustStr(const RustStr &other) noexcept; - RustStr &operator=(RustStr other) noexcept; + Str() noexcept; + Str(const char *s); + Str(const std::string &s); + Str(std::string &&s) = delete; + Str(const Str &other) noexcept; + Str &operator=(Str other) noexcept; operator std::string() const; // Note: no null terminator. @@ -54,7 +52,7 @@ public: const char *ptr; size_t len; }; - RustStr(Repr repr) noexcept; + Str(Repr repr) noexcept; operator Repr() noexcept; private: @@ -63,15 +61,15 @@ private: #ifndef CXXBRIDGE01_RUST_BOX #define CXXBRIDGE01_RUST_BOX -template class RustBox final { +template class Box final { public: - RustBox(const RustBox &other) : RustBox(*other) {} - RustBox(RustBox &&other) noexcept : repr(other.repr) { other.repr = 0; } - RustBox(const T &val) { + Box(const Box &other) : Box(*other) {} + Box(Box &&other) noexcept : repr(other.repr) { other.repr = 0; } + Box(const T &val) { this->uninit(); ::new (this->deref_mut()) T(val); } - RustBox &operator=(const RustBox &other) { + Box &operator=(const Box &other) { if (this != &other) { if (this->repr) { **this = *other; @@ -82,7 +80,7 @@ public: } return *this; } - RustBox &operator=(RustBox &&other) noexcept { + Box &operator=(Box &&other) noexcept { if (this->repr) { this->drop(); } @@ -90,7 +88,7 @@ public: other.repr = 0; return *this; } - ~RustBox() noexcept { + ~Box() noexcept { if (this->repr) { this->drop(); } @@ -103,8 +101,8 @@ public: // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. - static RustBox from_raw(T *raw) noexcept { - RustBox box; + static Box from_raw(T *raw) noexcept { + Box box; box.set_raw(raw); return box; } @@ -116,7 +114,7 @@ public: } private: - RustBox() noexcept {} + Box() noexcept {} void uninit() noexcept; void set_raw(T *) noexcept; T *get_raw() noexcept; @@ -127,7 +125,8 @@ private: }; #endif // CXXBRIDGE01_RUST_BOX -std::ostream &operator<<(std::ostream &os, const RustString &s); -std::ostream &operator<<(std::ostream &os, const RustStr &s); +std::ostream &operator<<(std::ostream &os, const String &s); +std::ostream &operator<<(std::ostream &os, const Str &s); -} // namespace cxxbridge01 +} // inline namespace cxxbridge01 +} // namespace rust diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index a502d20..c139688 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -3,8 +3,6 @@ #include #include -namespace cxxbridge = cxxbridge01; - extern "C" { const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -14,56 +12,57 @@ size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { return s.length(); } -// RustString -void cxxbridge01$rust_string$new(cxxbridge::RustString *self) noexcept; -void cxxbridge01$rust_string$clone(cxxbridge::RustString *self, - const cxxbridge::RustString &other) noexcept; -bool cxxbridge01$rust_string$from(cxxbridge::RustString *self, const char *ptr, +// rust::String +void cxxbridge01$rust_string$new(rust::String *self) noexcept; +void cxxbridge01$rust_string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge01$rust_string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge01$rust_string$drop(cxxbridge::RustString *self) noexcept; +void cxxbridge01$rust_string$drop(rust::String *self) noexcept; const char * -cxxbridge01$rust_string$ptr(const cxxbridge::RustString *self) noexcept; -size_t cxxbridge01$rust_string$len(const cxxbridge::RustString *self) noexcept; +cxxbridge01$rust_string$ptr(const rust::String *self) noexcept; +size_t cxxbridge01$rust_string$len(const rust::String *self) noexcept; -// RustStr +// rust::Str bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; } // extern "C" -namespace cxxbridge01 { +namespace rust { +inline namespace cxxbridge01 { -RustString::RustString() noexcept { cxxbridge01$rust_string$new(this); } +String::String() noexcept { cxxbridge01$rust_string$new(this); } -RustString::RustString(const RustString &other) noexcept { +String::String(const String &other) noexcept { cxxbridge01$rust_string$clone(this, other); } -RustString::RustString(RustString &&other) noexcept { +String::String(String &&other) noexcept { this->repr = other.repr; cxxbridge01$rust_string$new(&other); } -RustString::RustString(const char *s) { +String::String(const char *s) { auto len = strlen(s); if (!cxxbridge01$rust_string$from(this, s, len)) { - throw std::invalid_argument("data for RustString is not utf-8"); + throw std::invalid_argument("data for rust::String is not utf-8"); } } -RustString::RustString(const std::string &s) { +String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); if (!cxxbridge01$rust_string$from(this, ptr, len)) { - throw std::invalid_argument("data for RustString is not utf-8"); + throw std::invalid_argument("data for rust::String is not utf-8"); } } -RustString::~RustString() noexcept { cxxbridge01$rust_string$drop(this); } +String::~String() noexcept { cxxbridge01$rust_string$drop(this); } -RustString::operator std::string() const { +String::operator std::string() const { return std::string(this->data(), this->size()); } -RustString &RustString::operator=(const RustString &other) noexcept { +String &String::operator=(const String &other) noexcept { if (this != &other) { cxxbridge01$rust_string$drop(this); cxxbridge01$rust_string$clone(this, other); @@ -71,7 +70,7 @@ RustString &RustString::operator=(const RustString &other) noexcept { return *this; } -RustString &RustString::operator=(RustString &&other) noexcept { +String &String::operator=(String &&other) noexcept { if (this != &other) { cxxbridge01$rust_string$drop(this); this->repr = other.repr; @@ -80,65 +79,66 @@ RustString &RustString::operator=(RustString &&other) noexcept { return *this; } -const char *RustString::data() const noexcept { +const char *String::data() const noexcept { return cxxbridge01$rust_string$ptr(this); } -size_t RustString::size() const noexcept { +size_t String::size() const noexcept { return cxxbridge01$rust_string$len(this); } -size_t RustString::length() const noexcept { +size_t String::length() const noexcept { return cxxbridge01$rust_string$len(this); } -std::ostream &operator<<(std::ostream &os, const RustString &s) { +std::ostream &operator<<(std::ostream &os, const String &s) { os.write(s.data(), s.size()); return os; } -RustStr::RustStr() noexcept +Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} -RustStr::RustStr(const char *s) : repr(Repr{s, strlen(s)}) { +Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for RustStr is not utf-8"); + throw std::invalid_argument("data for rust::Str is not utf-8"); } } -RustStr::RustStr(const std::string &s) : repr(Repr{s.data(), s.length()}) { +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for RustStr is not utf-8"); + throw std::invalid_argument("data for rust::Str is not utf-8"); } } -RustStr::RustStr(const RustStr &) noexcept = default; +Str::Str(const Str &) noexcept = default; -RustStr &RustStr::operator=(RustStr other) noexcept { +Str &Str::operator=(Str other) noexcept { this->repr = other.repr; return *this; } -RustStr::operator std::string() const { +Str::operator std::string() const { return std::string(this->data(), this->size()); } -const char *RustStr::data() const noexcept { return this->repr.ptr; } +const char *Str::data() const noexcept { return this->repr.ptr; } -size_t RustStr::size() const noexcept { return this->repr.len; } +size_t Str::size() const noexcept { return this->repr.len; } -size_t RustStr::length() const noexcept { return this->repr.len; } +size_t Str::length() const noexcept { return this->repr.len; } -RustStr::RustStr(Repr repr_) noexcept : repr(repr_) {} +Str::Str(Repr repr_) noexcept : repr(repr_) {} -RustStr::operator Repr() noexcept { return this->repr; } +Str::operator Repr() noexcept { return this->repr; } -std::ostream &operator<<(std::ostream &os, const RustStr &s) { +std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); return os; } -} // namespace cxxbridge01 +} // inline namespace cxxbridge01 +} // namespace rust extern "C" { void cxxbridge01$unique_ptr$std$string$null( diff --git a/src/lib.rs b/src/lib.rs index 4c0f3c9..006159a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,10 +302,10 @@ //! //! //! -//! -//! +//! +//! //! -//! +//! //! //! //!
name in Rustname in C++restrictions
Stringcxxbridge::RustString
&strcxxbridge::RustStr
Stringrust::String
&strrust::Str
CxxStringstd::stringcannot be passed by value
Box<T>cxxbridge::RustBox<T>cannot hold opaque C++ type
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d27df41..65640df 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -19,12 +19,12 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } -cxxbridge::RustStr c_return_str(const Shared &shared) { +rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; } -cxxbridge::RustString c_return_rust_string() { return "2020"; } +rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); @@ -42,7 +42,7 @@ void c_take_shared(Shared shared) { } } -void c_take_box(cxxbridge::RustBox r) { +void c_take_box(rust::Box r) { (void)r; cxx_test_suite_set_correct(); } @@ -61,13 +61,13 @@ void c_take_ref_c(const C &c) { } } -void c_take_str(cxxbridge::RustStr s) { +void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } } -void c_take_rust_string(cxxbridge::RustString s) { +void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -99,8 +99,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); - r_take_str(cxxbridge::RustStr("2020")); - // TODO r_take_rust_string(cxxbridge::RustString("2020")); + r_take_str(rust::Str("2020")); + // TODO r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 87aac67..f41cc5e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,21 +19,21 @@ private: size_t c_return_primitive(); Shared c_return_shared(); -cxxbridge::RustBox c_return_box(); +rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); -cxxbridge::RustStr c_return_str(const Shared &shared); -cxxbridge::RustString c_return_rust_string(); +rust::Str c_return_str(const Shared &shared); +rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); -void c_take_box(cxxbridge::RustBox r); +void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); -void c_take_str(cxxbridge::RustStr s); -void c_take_rust_string(cxxbridge::RustString s); +void c_take_str(rust::Str s); +void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); } // namespace tests From 69fe4c25ec4162b2a5cedd26ce9013cec756ddd2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:05:01 +0000 Subject: [PATCH 74/2232] Format with clang-format --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index a668a66..3107668 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -128,5 +128,5 @@ private: std::ostream &operator<<(std::ostream &os, const String &s); std::ostream &operator<<(std::ostream &os, const Str &s); -} // inline namespace cxxbridge01 +} // namespace cxxbridge01 } // namespace rust diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index c139688..2952f92 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -19,8 +19,7 @@ void cxxbridge01$rust_string$clone(rust::String *self, bool cxxbridge01$rust_string$from(rust::String *self, const char *ptr, size_t len) noexcept; void cxxbridge01$rust_string$drop(rust::String *self) noexcept; -const char * -cxxbridge01$rust_string$ptr(const rust::String *self) noexcept; +const char *cxxbridge01$rust_string$ptr(const rust::String *self) noexcept; size_t cxxbridge01$rust_string$len(const rust::String *self) noexcept; // rust::Str @@ -96,8 +95,7 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -Str::Str() noexcept - : repr(Repr{reinterpret_cast(this), 0}) {} +Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { @@ -137,7 +135,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } -} // inline namespace cxxbridge01 +} // namespace cxxbridge01 } // namespace rust extern "C" { From 9ad1fbc47843a13550438c1a3d26a24b8ccb0c87 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:05:01 +0000 Subject: [PATCH 75/2232] Pass ending comment to end_block Clang-format doesn't always like the same ending comment as the start of the block. In particular it wants: inline namespace cxxbridge01 { ... } // namespace cxxbridge01 --- diff --git a/gen/out.rs b/gen/out.rs index 8d7499b..506ce6c 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -5,8 +5,7 @@ pub(crate) struct OutFile { pub header: bool, content: Vec, section_pending: bool, - blocks: Vec<&'static str>, - blocks_pending: usize, + blocks_pending: Vec<&'static str>, } impl OutFile { @@ -16,8 +15,7 @@ impl OutFile { header, content: Vec::new(), section_pending: false, - blocks: Vec::new(), - blocks_pending: 0, + blocks_pending: Vec::new(), } } @@ -27,17 +25,13 @@ impl OutFile { } pub fn begin_block(&mut self, block: &'static str) { - self.blocks.push(block); - self.blocks_pending += 1; + self.blocks_pending.push(block); } - pub fn end_block(&mut self) { - if self.blocks_pending > 0 { - self.blocks_pending -= 1; - } else { + pub fn end_block(&mut self, block: &'static str) { + if self.blocks_pending.pop().is_none() { self.content.extend_from_slice(b"} // "); - self.content - .extend_from_slice(self.blocks.pop().unwrap().as_bytes()); + self.content.extend_from_slice(block.as_bytes()); self.content.push(b'\n'); self.section_pending = true; } @@ -51,13 +45,12 @@ impl OutFile { impl Write for OutFile { fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { - if self.blocks_pending > 0 { + if !self.blocks_pending.is_empty() { self.content.push(b'\n'); - for block in &self.blocks[self.blocks.len() - self.blocks_pending..] { + for block in self.blocks_pending.drain(..) { self.content.extend_from_slice(block.as_bytes()); self.content.extend_from_slice(b" {\n"); } - self.blocks_pending = 0; self.section_pending = false; } else if self.section_pending { self.content.push(b'\n'); diff --git a/gen/write.rs b/gen/write.rs index a2c1b7a..99ea418 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -54,7 +54,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b out.next_section(); write(out, efn, types); } - out.end_block(); + out.end_block("extern \"C\""); } for api in apis { @@ -125,8 +125,8 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } } - out.end_block(); - out.end_block(); + out.end_block("namespace cxxbridge01"); + out.end_block("namespace rust"); } fn write_struct(out: &mut OutFile, strct: &Struct) { @@ -434,7 +434,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block(); + out.end_block("extern \"C\""); out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge01"); @@ -445,8 +445,8 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block(); - out.end_block(); + out.end_block("namespace cxxbridge01"); + out.end_block("namespace rust"); } fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { From 3b0c98805944aaaa404208e1cd66bec8c6216636 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:08:57 +0000 Subject: [PATCH 76/2232] Expose snake case type aliases --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 3107668..4fc1bf9 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -128,5 +128,10 @@ private: std::ostream &operator<<(std::ostream &os, const String &s); std::ostream &operator<<(std::ostream &os, const Str &s); +// Snake case aliases for use in code that uses this style for type names. +using string = String; +using str = Str; +template using box = Box; + } // namespace cxxbridge01 } // namespace rust From c2db0e83f2e5745276a76c16a82750b9369d6e29 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:09:19 +0000 Subject: [PATCH 77/2232] Demonstrate that snake case type aliases work --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index b975e88..e6a5d08 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,7 +9,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(rust::Str appname) { +std::unique_ptr make_demo(rust::str appname) { return std::unique_ptr(new ThingC(appname)); } diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index a579986..9a3bcd0 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -16,7 +16,7 @@ public: struct SharedThing; -std::unique_ptr make_demo(rust::Str appname); +std::unique_ptr make_demo(rust::str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 65640df..ecc4488 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -19,12 +19,12 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } -rust::Str c_return_str(const Shared &shared) { +rust::str c_return_str(const Shared &shared) { (void)shared; return "2020"; } -rust::String c_return_rust_string() { return "2020"; } +rust::string c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); @@ -42,7 +42,7 @@ void c_take_shared(Shared shared) { } } -void c_take_box(rust::Box r) { +void c_take_box(rust::box r) { (void)r; cxx_test_suite_set_correct(); } @@ -61,13 +61,13 @@ void c_take_ref_c(const C &c) { } } -void c_take_str(rust::Str s) { +void c_take_str(rust::str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } } -void c_take_rust_string(rust::String s) { +void c_take_rust_string(rust::string s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -99,8 +99,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); - r_take_str(rust::Str("2020")); - // TODO r_take_rust_string(rust::String("2020")); + r_take_str(rust::str("2020")); + // TODO r_take_rust_string(rust::string("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f41cc5e..3bb5095 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,21 +19,21 @@ private: size_t c_return_primitive(); Shared c_return_shared(); -rust::Box c_return_box(); +rust::box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); -rust::Str c_return_str(const Shared &shared); -rust::String c_return_rust_string(); +rust::str c_return_str(const Shared &shared); +rust::string c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); -void c_take_box(rust::Box r); +void c_take_box(rust::box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); -void c_take_str(rust::Str s); -void c_take_rust_string(rust::String s); +void c_take_str(rust::str s); +void c_take_rust_string(rust::string s); void c_take_unique_ptr_string(std::unique_ptr s); } // namespace tests From 92d12f3993921b33c83a9cdb32f962297f5fb11d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:13:50 +0000 Subject: [PATCH 78/2232] Revert "Demonstrate that snake case type aliases work" This reverts commit c2db0e83f2e5745276a76c16a82750b9369d6e29. --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index e6a5d08..b975e88 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,7 +9,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(rust::str appname) { +std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(appname)); } diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 9a3bcd0..a579986 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -16,7 +16,7 @@ public: struct SharedThing; -std::unique_ptr make_demo(rust::str appname); +std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index ecc4488..65640df 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -19,12 +19,12 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } -rust::str c_return_str(const Shared &shared) { +rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; } -rust::string c_return_rust_string() { return "2020"; } +rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); @@ -42,7 +42,7 @@ void c_take_shared(Shared shared) { } } -void c_take_box(rust::box r) { +void c_take_box(rust::Box r) { (void)r; cxx_test_suite_set_correct(); } @@ -61,13 +61,13 @@ void c_take_ref_c(const C &c) { } } -void c_take_str(rust::str s) { +void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } } -void c_take_rust_string(rust::string s) { +void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); } @@ -99,8 +99,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_shared(Shared{2020}); r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); - r_take_str(rust::str("2020")); - // TODO r_take_rust_string(rust::string("2020")); + r_take_str(rust::Str("2020")); + // TODO r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 3bb5095..f41cc5e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,21 +19,21 @@ private: size_t c_return_primitive(); Shared c_return_shared(); -rust::box c_return_box(); +rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); -rust::str c_return_str(const Shared &shared); -rust::string c_return_rust_string(); +rust::Str c_return_str(const Shared &shared); +rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); -void c_take_box(rust::box r); +void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); -void c_take_str(rust::str s); -void c_take_rust_string(rust::string s); +void c_take_str(rust::Str s); +void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); } // namespace tests From da2b9045308b76ca5f5fe89be64b021db138cce1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 01 2020 22:42:19 +0000 Subject: [PATCH 79/2232] Merge pull request #50 from dtolnay/snake Expose snake case type aliases --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 3107668..4fc1bf9 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -128,5 +128,10 @@ private: std::ostream &operator<<(std::ostream &os, const String &s); std::ostream &operator<<(std::ostream &os, const Str &s); +// Snake case aliases for use in code that uses this style for type names. +using string = String; +using str = Str; +template using box = Box; + } // namespace cxxbridge01 } // namespace rust From 9081beb1a01f4150937bd97efdec631cf1ad03bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 03:52:09 +0000 Subject: [PATCH 80/2232] Remove redundant prefix from mangled Box related symbols --- diff --git a/gen/write.rs b/gen/write.rs index 99ea418..381800b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -462,27 +462,27 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$rust_box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge01$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$rust_box${}$set_raw(::rust::Box<{}> *ptr, {} *raw) noexcept;", + "void cxxbridge01$box${}$set_raw(::rust::Box<{}> *ptr, {} *raw) noexcept;", instance, inner, inner ); writeln!( out, - "void cxxbridge01$rust_box${}$drop(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge01$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge01$rust_box${}$deref(const ::rust::Box<{}> *ptr) noexcept;", + "const {} *cxxbridge01$box${}$deref(const ::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!( out, - "{} *cxxbridge01$rust_box${}$deref_mut(::rust::Box<{}> *ptr) noexcept;", + "{} *cxxbridge01$box${}$deref_mut(::rust::Box<{}> *ptr) noexcept;", inner, instance, inner, ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); @@ -499,11 +499,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!( - out, - " return cxxbridge01$rust_box${}$uninit(this);", - instance - ); + writeln!(out, " return cxxbridge01$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); @@ -514,18 +510,14 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - " return cxxbridge01$rust_box${}$set_raw(this, raw);", + " return cxxbridge01$box${}$set_raw(this, raw);", instance ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!( - out, - " return cxxbridge01$rust_box${}$drop(this);", - instance - ); + writeln!(out, " return cxxbridge01$box${}$drop(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); @@ -534,18 +526,14 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { "const {} *Box<{}>::deref() const noexcept {{", inner, inner, ); - writeln!( - out, - " return cxxbridge01$rust_box${}$deref(this);", - instance - ); + writeln!(out, " return cxxbridge01$box${}$deref(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "{} *Box<{}>::deref_mut() noexcept {{", inner, inner); writeln!( out, - " return cxxbridge01$rust_box${}$deref_mut(this);", + " return cxxbridge01$box${}$deref_mut(this);", instance ); writeln!(out, "}}"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index bcd02f1..1896a13 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -293,7 +293,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge01$rust_box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge01$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_set_raw = format!("{}set_raw", link_prefix); let link_drop = format!("{}drop", link_prefix); From 001102af7137959a85af9ba0c802a97502bf879e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:05:04 +0000 Subject: [PATCH 81/2232] Use iosfwd for forward declared ostream insertion operators --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 4fc1bf9..c8f85a9 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include namespace rust { diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 2952f92..7991bfe 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -1,5 +1,6 @@ #include "../include/cxxbridge.h" #include +#include #include #include From 83a5a11b9a5b3bf64969fc9ce544d0aafd2f6037 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:18:37 +0000 Subject: [PATCH 82/2232] Merge pull request #55 from dtolnay/iosfwd Use iosfwd for forward declared ostream insertion operators --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 4fc1bf9..c8f85a9 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include namespace rust { diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 2952f92..7991bfe 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -1,5 +1,6 @@ #include "../include/cxxbridge.h" #include +#include #include #include From 404d689e953cfe3e221a242e762f6eaa8b02f18e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:19:41 +0000 Subject: [PATCH 83/2232] Change string cast operators to explicit --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index b975e88..04287b8 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -10,7 +10,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } std::unique_ptr make_demo(rust::Str appname) { - return std::unique_ptr(new ThingC(appname)); + return std::unique_ptr(new ThingC(std::string(appname))); } const std::string &get_name(const ThingC &thing) { return thing.appname; } diff --git a/include/cxxbridge.h b/include/cxxbridge.h index c8f85a9..b5a6451 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -17,7 +17,7 @@ public: String &operator=(const String &other) noexcept; String &operator=(String &&other) noexcept; ~String() noexcept; - operator std::string() const; + explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; @@ -37,7 +37,7 @@ public: Str(std::string &&s) = delete; Str(const Str &other) noexcept; Str &operator=(Str other) noexcept; - operator std::string() const; + explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; From baae443ae2af1cf14c613389c33f5a0f015348d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:20:10 +0000 Subject: [PATCH 84/2232] Change Str to Repr operator to explicit --- diff --git a/gen/write.rs b/gen/write.rs index 381800b..e0396c9 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -191,8 +191,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "("); } else if let Some(ret) = &efn.ret { write!(out, "return "); - if let Type::Ref(_) = ret { - write!(out, "&"); + match ret { + Type::Ref(_) => write!(out, "&"), + Type::Str(_) => write!(out, "::rust::Str::Repr("), + _ => {} } } write!(out, "{}$(", efn.ident); @@ -216,6 +218,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::Str(_)) => write!(out, ")"), _ => {} } if indirect_return { @@ -293,13 +296,16 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if i > 0 { write!(out, ", "); } - if types.needs_indirect_abi(&arg.ty) { - write!(out, "&"); + match &arg.ty { + Type::Str(_) => write!(out, "::rust::Str::Repr("), + ty if types.needs_indirect_abi(ty) => write!(out, "&"), + _ => {} } write!(out, "{}", arg.ident); match arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), + Type::Str(_) => write!(out, ")"), _ => {} } } diff --git a/include/cxxbridge.h b/include/cxxbridge.h index b5a6451..a0c0b07 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -53,7 +53,7 @@ public: size_t len; }; Str(Repr repr) noexcept; - operator Repr() noexcept; + explicit operator Repr() noexcept; private: Repr repr; From 133d9d5d9d8910d1071470d0cd1ac187a0ee9671 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:36:12 +0000 Subject: [PATCH 85/2232] Merge pull request #56 from dtolnay/cast Change cast operators to explicit --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index b975e88..04287b8 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -10,7 +10,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } std::unique_ptr make_demo(rust::Str appname) { - return std::unique_ptr(new ThingC(appname)); + return std::unique_ptr(new ThingC(std::string(appname))); } const std::string &get_name(const ThingC &thing) { return thing.appname; } diff --git a/gen/write.rs b/gen/write.rs index 381800b..e0396c9 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -191,8 +191,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, "("); } else if let Some(ret) = &efn.ret { write!(out, "return "); - if let Type::Ref(_) = ret { - write!(out, "&"); + match ret { + Type::Ref(_) => write!(out, "&"), + Type::Str(_) => write!(out, "::rust::Str::Repr("), + _ => {} } } write!(out, "{}$(", efn.ident); @@ -216,6 +218,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::Str(_)) => write!(out, ")"), _ => {} } if indirect_return { @@ -293,13 +296,16 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if i > 0 { write!(out, ", "); } - if types.needs_indirect_abi(&arg.ty) { - write!(out, "&"); + match &arg.ty { + Type::Str(_) => write!(out, "::rust::Str::Repr("), + ty if types.needs_indirect_abi(ty) => write!(out, "&"), + _ => {} } write!(out, "{}", arg.ident); match arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), + Type::Str(_) => write!(out, ")"), _ => {} } } diff --git a/include/cxxbridge.h b/include/cxxbridge.h index c8f85a9..a0c0b07 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -17,7 +17,7 @@ public: String &operator=(const String &other) noexcept; String &operator=(String &&other) noexcept; ~String() noexcept; - operator std::string() const; + explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; @@ -37,7 +37,7 @@ public: Str(std::string &&s) = delete; Str(const Str &other) noexcept; Str &operator=(Str other) noexcept; - operator std::string() const; + explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; @@ -53,7 +53,7 @@ public: size_t len; }; Str(Repr repr) noexcept; - operator Repr() noexcept; + explicit operator Repr() noexcept; private: Repr repr; From d9c4ac955f835eae3c7bbaf6feedf13b027e58bb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 04:36:21 +0000 Subject: [PATCH 86/2232] Organize string constructors --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index a0c0b07..984eca2 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -10,13 +10,16 @@ inline namespace cxxbridge01 { class String final { public: String() noexcept; - String(const String &other) noexcept; - String(String &&other) noexcept; - String(const char *s); - String(const std::string &s); - String &operator=(const String &other) noexcept; - String &operator=(String &&other) noexcept; + String(const String &) noexcept; + String(String &&) noexcept; ~String() noexcept; + + String(const std::string &); + String(const char *); + + String &operator=(const String &) noexcept; + String &operator=(String &&) noexcept; + explicit operator std::string() const; // Note: no null terminator. @@ -32,11 +35,14 @@ private: class Str final { public: Str() noexcept; - Str(const char *s); + Str(const Str &) noexcept; + Str(const std::string &s); + Str(const char *s); Str(std::string &&s) = delete; - Str(const Str &other) noexcept; - Str &operator=(Str other) noexcept; + + Str &operator=(Str) noexcept; + explicit operator std::string() const; // Note: no null terminator. @@ -52,7 +58,7 @@ public: const char *ptr; size_t len; }; - Str(Repr repr) noexcept; + Str(Repr) noexcept; explicit operator Repr() noexcept; private: diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 7991bfe..1f7c8dd 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -41,12 +41,7 @@ String::String(String &&other) noexcept { cxxbridge01$rust_string$new(&other); } -String::String(const char *s) { - auto len = strlen(s); - if (!cxxbridge01$rust_string$from(this, s, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); - } -} +String::~String() noexcept { cxxbridge01$rust_string$drop(this); } String::String(const std::string &s) { auto ptr = s.data(); @@ -56,10 +51,11 @@ String::String(const std::string &s) { } } -String::~String() noexcept { cxxbridge01$rust_string$drop(this); } - -String::operator std::string() const { - return std::string(this->data(), this->size()); +String::String(const char *s) { + auto len = strlen(s); + if (!cxxbridge01$rust_string$from(this, s, len)) { + throw std::invalid_argument("data for rust::String is not utf-8"); + } } String &String::operator=(const String &other) noexcept { @@ -79,6 +75,10 @@ String &String::operator=(String &&other) noexcept { return *this; } +String::operator std::string() const { + return std::string(this->data(), this->size()); +} + const char *String::data() const noexcept { return cxxbridge01$rust_string$ptr(this); } @@ -98,20 +98,20 @@ std::ostream &operator<<(std::ostream &os, const String &s) { Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} -Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { +Str::Str(const Str &) noexcept = default; + +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { +Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } -Str::Str(const Str &) noexcept = default; - Str &Str::operator=(Str other) noexcept { this->repr = other.repr; return *this; From f6292378c714cd727e31bf6fc22d1233305ca4c1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 05:09:11 +0000 Subject: [PATCH 87/2232] Add rust::Box member type aliases --- diff --git a/gen/write.rs b/gen/write.rs index e0396c9..e6fb4e9 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -79,6 +79,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b fn write_includes(out: &mut OutFile, types: &Types) { let mut has_int = false; + let mut has_box = false; let mut has_unique_ptr = false; let mut has_string = false; @@ -90,6 +91,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(CxxString) => has_string = true, Some(Bool) | Some(RustString) | None => {} }, + Type::RustBox(_) => has_box = true, Type::UniquePtr(_) => has_unique_ptr = true, _ => {} } @@ -104,6 +106,9 @@ fn write_includes(out: &mut OutFile, types: &Types) { if has_string { writeln!(out, "#include "); } + if has_box { + writeln!(out, "#include "); + } } fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 984eca2..fe2f5be 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace rust { inline namespace cxxbridge01 { @@ -69,6 +70,10 @@ private: #define CXXBRIDGE01_RUST_BOX template class Box final { public: + using value_type = T; + using const_pointer = std::add_pointer_t>; + using pointer = std::add_pointer_t; + Box(const Box &other) : Box(*other) {} Box(Box &&other) noexcept : repr(other.repr) { other.repr = 0; } Box(const T &val) { From 9f9213709d6cd9091aa7d1a9763611307093ff40 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 05:09:40 +0000 Subject: [PATCH 88/2232] Backport type aliases to c++11 --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index fe2f5be..d0cf8c2 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -71,8 +71,9 @@ private: template class Box final { public: using value_type = T; - using const_pointer = std::add_pointer_t>; - using pointer = std::add_pointer_t; + using const_pointer = typename std::add_pointer< + typename std::add_const::type>::type; + using pointer = typename std::add_pointer::type; Box(const Box &other) : Box(*other) {} Box(Box &&other) noexcept : repr(other.repr) { other.repr = 0; } From 2248c3064e9654dddd8eb8f4fa4bf187354b19bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 05:13:32 +0000 Subject: [PATCH 89/2232] Write Box internals in terms of type aliases --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index d0cf8c2..aa78c2b 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -128,11 +128,11 @@ public: private: Box() noexcept {} void uninit() noexcept; - void set_raw(T *) noexcept; - T *get_raw() noexcept; + void set_raw(pointer) noexcept; + pointer get_raw() noexcept; void drop() noexcept; - const T *deref() const noexcept; - T *deref_mut() noexcept; + const_pointer deref() const noexcept; + pointer deref_mut() noexcept; uintptr_t repr; }; #endif // CXXBRIDGE01_RUST_BOX From 851677c4cc3a5c71c35f518f06187a488e3b510c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 07:49:46 +0000 Subject: [PATCH 90/2232] Remove insignificant parameter names from header --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index aa78c2b..d759c9d 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -38,9 +38,9 @@ public: Str() noexcept; Str(const Str &) noexcept; - Str(const std::string &s); - Str(const char *s); - Str(std::string &&s) = delete; + Str(const std::string &); + Str(const char *); + Str(std::string &&) = delete; Str &operator=(Str) noexcept; @@ -137,8 +137,8 @@ private: }; #endif // CXXBRIDGE01_RUST_BOX -std::ostream &operator<<(std::ostream &os, const String &s); -std::ostream &operator<<(std::ostream &os, const Str &s); +std::ostream &operator<<(std::ostream &, const String &); +std::ostream &operator<<(std::ostream &, const Str &); // Snake case aliases for use in code that uses this style for type names. using string = String; From 4590d2af81b0b80af55ee6d9825397d106b63723 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 08:03:25 +0000 Subject: [PATCH 91/2232] Delete unused get_raw member from Box --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index d759c9d..1701f4d 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -129,7 +129,6 @@ private: Box() noexcept {} void uninit() noexcept; void set_raw(pointer) noexcept; - pointer get_raw() noexcept; void drop() noexcept; const_pointer deref() const noexcept; pointer deref_mut() noexcept; From 6c089108e4a1e7fd0cf3e8c61f2d8bf2cdc9bb2f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 02 2020 08:23:05 +0000 Subject: [PATCH 92/2232] Remove prefix from mangled string symbols --- diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index 1f7c8dd..c58b7c5 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -14,63 +14,63 @@ size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { } // rust::String -void cxxbridge01$rust_string$new(rust::String *self) noexcept; -void cxxbridge01$rust_string$clone(rust::String *self, - const rust::String &other) noexcept; -bool cxxbridge01$rust_string$from(rust::String *self, const char *ptr, - size_t len) noexcept; -void cxxbridge01$rust_string$drop(rust::String *self) noexcept; -const char *cxxbridge01$rust_string$ptr(const rust::String *self) noexcept; -size_t cxxbridge01$rust_string$len(const rust::String *self) noexcept; +void cxxbridge01$string$new(rust::String *self) noexcept; +void cxxbridge01$string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge01$string$from(rust::String *self, const char *ptr, + size_t len) noexcept; +void cxxbridge01$string$drop(rust::String *self) noexcept; +const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge01$string$len(const rust::String *self) noexcept; // rust::Str -bool cxxbridge01$rust_str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; } // extern "C" namespace rust { inline namespace cxxbridge01 { -String::String() noexcept { cxxbridge01$rust_string$new(this); } +String::String() noexcept { cxxbridge01$string$new(this); } String::String(const String &other) noexcept { - cxxbridge01$rust_string$clone(this, other); + cxxbridge01$string$clone(this, other); } String::String(String &&other) noexcept { this->repr = other.repr; - cxxbridge01$rust_string$new(&other); + cxxbridge01$string$new(&other); } -String::~String() noexcept { cxxbridge01$rust_string$drop(this); } +String::~String() noexcept { cxxbridge01$string$drop(this); } String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); - if (!cxxbridge01$rust_string$from(this, ptr, len)) { + if (!cxxbridge01$string$from(this, ptr, len)) { throw std::invalid_argument("data for rust::String is not utf-8"); } } String::String(const char *s) { auto len = strlen(s); - if (!cxxbridge01$rust_string$from(this, s, len)) { + if (!cxxbridge01$string$from(this, s, len)) { throw std::invalid_argument("data for rust::String is not utf-8"); } } String &String::operator=(const String &other) noexcept { if (this != &other) { - cxxbridge01$rust_string$drop(this); - cxxbridge01$rust_string$clone(this, other); + cxxbridge01$string$drop(this); + cxxbridge01$string$clone(this, other); } return *this; } String &String::operator=(String &&other) noexcept { if (this != &other) { - cxxbridge01$rust_string$drop(this); + cxxbridge01$string$drop(this); this->repr = other.repr; - cxxbridge01$rust_string$new(&other); + cxxbridge01$string$new(&other); } return *this; } @@ -80,16 +80,12 @@ String::operator std::string() const { } const char *String::data() const noexcept { - return cxxbridge01$rust_string$ptr(this); + return cxxbridge01$string$ptr(this); } -size_t String::size() const noexcept { - return cxxbridge01$rust_string$len(this); -} +size_t String::size() const noexcept { return cxxbridge01$string$len(this); } -size_t String::length() const noexcept { - return cxxbridge01$rust_string$len(this); -} +size_t String::length() const noexcept { return cxxbridge01$string$len(this); } std::ostream &operator<<(std::ostream &os, const String &s) { os.write(s.data(), s.size()); @@ -101,13 +97,13 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const Str &) noexcept = default; Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { - if (!cxxbridge01$rust_str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } diff --git a/src/rust_str.rs b/src/rust_str.rs index 59a7784..3d8a9f0 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -23,7 +23,7 @@ impl RustStr { } } -#[export_name = "cxxbridge01$rust_str$valid"] +#[export_name = "cxxbridge01$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/rust_string.rs b/src/rust_string.rs index 250a46f..43cd5a6 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -26,17 +26,17 @@ impl RustString { } } -#[export_name = "cxxbridge01$rust_string$new"] +#[export_name = "cxxbridge01$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge01$rust_string$clone"] +#[export_name = "cxxbridge01$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge01$rust_string$from"] +#[export_name = "cxxbridge01$string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -52,17 +52,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge01$rust_string$drop"] +#[export_name = "cxxbridge01$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge01$rust_string$ptr"] +#[export_name = "cxxbridge01$string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge01$rust_string$len"] +#[export_name = "cxxbridge01$string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } From 40226ab58adb35ed90ff9c313fefbca8e3c4ae2c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 03 2020 08:05:35 +0000 Subject: [PATCH 93/2232] Implement passing ownership of string to Rust --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1896a13..16dab26 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -233,21 +233,17 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type let args = efn.args.iter().map(|arg| expand_extern_arg(arg, types)); let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; - let var = if types.needs_indirect_abi(&arg.ty) { - quote!(::std::ptr::read(#ident)) - } else { - quote!(#ident) - }; match &arg.ty { - Type::Ident(ident) if ident == "String" => quote!(#var.into_string()), - Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#var)), - Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#var)), + Type::Ident(i) if i == "String" => quote!(::std::mem::take((*#ident).as_mut_string())), + Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), + Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == "String" => quote!(#var.as_string()), - _ => var, + Type::Ident(i) if i == "String" => quote!(#ident.as_string()), + _ => quote!(#ident), }, - Type::Str(_) => quote!(#var.as_str()), - _ => var, + Type::Str(_) => quote!(#ident.as_str()), + ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), + _ => quote!(#ident), } }); let mut outparam = None; diff --git a/src/rust_string.rs b/src/rust_string.rs index 43cd5a6..345d969 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -24,6 +24,10 @@ impl RustString { pub fn as_string(&self) -> &String { &self.repr } + + pub fn as_mut_string(&mut self) -> &mut String { + &mut self.repr + } } #[export_name = "cxxbridge01$string$new"] diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 65640df..c94e34d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -100,7 +100,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); - // TODO r_take_rust_string(rust::String("2020")); + r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); From 39d575fb04b235af187f786dbb04680fd77f9c9c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 03 2020 20:47:43 +0000 Subject: [PATCH 94/2232] Separate the two uses of expand_extern_arg These are going to need to diverge shortly. Indirect args can pass from Rust to C++ as *const T from which C++ will do an unsafe ptr::read, but need to pass from C++ to Rust as *mut T to leave a zero value in the old location for when the C++ destructor runs. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 16dab26..3d6eee0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,6 +1,6 @@ use crate::namespace::Namespace; use crate::syntax::atom::Atom; -use crate::syntax::{self, check, Api, ExternFn, ExternType, Struct, Type, Types, Var}; +use crate::syntax::{self, check, Api, ExternFn, ExternType, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{Error, ItemMod, Result, Token}; @@ -123,7 +123,15 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let args = efn.args.iter().map(|arg| expand_extern_arg(arg, types)); + let args = efn.args.iter().map(|arg| { + let ident = &arg.ident; + let ty = expand_extern_type(&arg.ty); + if types.needs_indirect_abi(&arg.ty) { + quote!(#ident: *mut #ty) + } else { + quote!(#ident: #ty) + } + }); let ret = expand_extern_return_type(&efn.ret, types); let mut outparam = None; if indirect_return(&efn.ret, types) { @@ -230,7 +238,15 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let args = efn.args.iter().map(|arg| expand_extern_arg(arg, types)); + let args = efn.args.iter().map(|arg| { + let ident = &arg.ident; + let ty = expand_extern_type(&arg.ty); + if types.needs_indirect_abi(&arg.ty) { + quote!(#ident: *mut #ty) + } else { + quote!(#ident: #ty) + } + }); let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { @@ -444,13 +460,3 @@ fn expand_extern_return_type(ret: &Option, types: &Types) -> TokenStream { let ty = expand_extern_type(ret); quote!(-> #ty) } - -fn expand_extern_arg(arg: &Var, types: &Types) -> TokenStream { - let ident = &arg.ident; - let ty = expand_extern_type(&arg.ty); - if types.needs_indirect_abi(&arg.ty) { - quote!(#ident: *mut #ty) - } else { - quote!(#ident: #ty) - } -} From ba5eb2de8cbd6fd2b033203b4c3c2d87e8b9f534 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:15:52 +0000 Subject: [PATCH 95/2232] Add PartialEq impls for comparison against specific atoms --- diff --git a/syntax/atom.rs b/syntax/atom.rs index 57325d8..903f561 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -1,3 +1,4 @@ +use crate::syntax::Type; use proc_macro2::Ident; #[derive(Copy, Clone, PartialEq)] @@ -38,3 +39,18 @@ impl Atom { } } } + +impl PartialEq for Ident { + fn eq(&self, atom: &Atom) -> bool { + Atom::from(self) == Some(*atom) + } +} + +impl PartialEq for Type { + fn eq(&self, atom: &Atom) -> bool { + match self { + Type::Ident(ident) => ident == atom, + _ => false, + } + } +} From 438e26034c8b62dcadb0f83e2ea32cdd7bd05d8f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:21:35 +0000 Subject: [PATCH 96/2232] Make PartialEq work with reference lhs --- diff --git a/syntax/atom.rs b/syntax/atom.rs index 903f561..9fb6554 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -54,3 +54,15 @@ impl PartialEq for Type { } } } + +impl PartialEq for &Ident { + fn eq(&self, atom: &Atom) -> bool { + *self == atom + } +} + +impl PartialEq for &Type { + fn eq(&self, atom: &Atom) -> bool { + *self == atom + } +} From a52602b8afb63e85db3a0b28df32dfcb7f430766 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:24:34 +0000 Subject: [PATCH 97/2232] Use PartialEq for some ident comparisons --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 3d6eee0..1237892 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,5 @@ use crate::namespace::Namespace; -use crate::syntax::atom::Atom; +use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{self, check, Api, ExternFn, ExternType, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; @@ -156,13 +156,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { - Type::Ident(ident) if ident == "String" => { + Type::Ident(ident) if ident == RustString => { quote!(#var.as_mut_ptr() as *mut ::cxx::private::RustString) } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == "String" => { + Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) } _ => quote!(#var), @@ -204,11 +204,11 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .ret .as_ref() .and_then(|ret| match ret { - Type::Ident(ident) if ident == "String" => Some(quote!(#call.into_string())), + Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == "String" => Some(quote!(#call.as_string())), + Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), @@ -250,11 +250,11 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { - Type::Ident(i) if i == "String" => quote!(::std::mem::take((*#ident).as_mut_string())), + Type::Ident(i) if i == RustString => quote!(::std::mem::take((*#ident).as_mut_string())), Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(i) if i == "String" => quote!(#ident.as_string()), + Type::Ident(i) if i == RustString => quote!(#ident.as_string()), _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), @@ -270,13 +270,13 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type .ret .as_ref() .and_then(|ret| match ret { - Type::Ident(ident) if ident == "String" => { + Type::Ident(ident) if ident == RustString => { Some(quote!(::cxx::private::RustString::from(#call))) } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == "String" => { + Type::Ident(ident) if ident == RustString => { Some(quote!(::cxx::private::RustString::from_ref(#call))) } _ => None, @@ -438,13 +438,13 @@ fn indirect_return(ret: &Option, types: &Types) -> bool { fn expand_extern_type(ty: &Type) -> TokenStream { match ty { - Type::Ident(ident) if ident == "String" => quote!(::cxx::private::RustString), + Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString), Type::RustBox(ty) | Type::UniquePtr(ty) => { let inner = &ty.inner; quote!(*mut #inner) } Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == "String" => quote!(&::cxx::private::RustString), + Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), diff --git a/syntax/check.rs b/syntax/check.rs index c2c6d41..df633de 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -96,7 +96,7 @@ fn is_unsized(ty: &Type, types: &Types) -> bool { Type::Ident(ident) => ident, _ => return false, }; - ident == "CxxString" || types.cxx.contains(ident) || types.rust.contains(ident) + ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) } fn check_mut_return_restriction(efn: &ExternFn) -> Result<()> { diff --git a/syntax/tokens.rs b/syntax/tokens.rs index f553207..e97509b 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,3 +1,4 @@ +use crate::syntax::atom::Atom::*; use crate::syntax::{Derive, ExternFn, Ref, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -7,7 +8,7 @@ impl ToTokens for Type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Type::Ident(ident) => { - if ident == "CxxString" { + if ident == CxxString { let span = ident.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } From d1e2efc8472f37e6de4fc59b05205b5b45449999 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:25:50 +0000 Subject: [PATCH 98/2232] Begin to introduce Rust-style move for C++ objects --- diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 1701f4d..1ac3133 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -8,6 +8,8 @@ namespace rust { inline namespace cxxbridge01 { +struct unsafe_bitcopy_t; + class String final { public: String() noexcept; @@ -28,6 +30,9 @@ public: size_t size() const noexcept; size_t length() const noexcept; + // Internal API only intended for the cxxbridge code generator. + String(unsafe_bitcopy_t, const String &) noexcept; + private: // Size and alignment statically verified by rust_string.rs. std::array repr; @@ -144,5 +149,10 @@ using string = String; using str = Str; template using box = Box; +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; + } // namespace cxxbridge01 } // namespace rust diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc index c58b7c5..b44204d 100644 --- a/src/cxxbridge.cc +++ b/src/cxxbridge.cc @@ -87,6 +87,9 @@ size_t String::size() const noexcept { return cxxbridge01$string$len(this); } size_t String::length() const noexcept { return cxxbridge01$string$len(this); } +String::String(unsafe_bitcopy_t, const String &bits) noexcept + : repr(bits.repr) {} + std::ostream &operator<<(std::ostream &os, const String &s) { os.write(s.data(), s.size()); return os; From a46a237fe7a9391a21b8251b3ff8ab4ceef2f461 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:28:15 +0000 Subject: [PATCH 99/2232] Make passing String by value to C++ const --- diff --git a/gen/write.rs b/gen/write.rs index e6fb4e9..33898ce 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -169,6 +169,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if i > 0 { write!(out, ", "); } + if arg.ty == RustString { + write!(out, "const "); + } write_extern_arg(out, arg, types); } if indirect_return { @@ -213,6 +216,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else if let Type::UniquePtr(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "({})", arg.ident); + } else if arg.ty == RustString { + write!(out, "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { write!(out, "::std::move(*{})", arg.ident); } else { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1237892..9ebb623 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -126,7 +126,9 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let args = efn.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); - if types.needs_indirect_abi(&arg.ty) { + if arg.ty == RustString { + quote!(#ident: *const #ty) + } else if types.needs_indirect_abi(&arg.ty) { quote!(#ident: *mut #ty) } else { quote!(#ident: #ty) @@ -157,7 +159,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let var = &arg.ident; match &arg.ty { Type::Ident(ident) if ident == RustString => { - quote!(#var.as_mut_ptr() as *mut ::cxx::private::RustString) + quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString) } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), From cc3767f0ed74a4e1d4c06a408c8f5ce3eca59b5c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 18:42:02 +0000 Subject: [PATCH 100/2232] Format with rustfmt 2019-10-07 --- diff --git a/gen/write.rs b/gen/write.rs index 33898ce..68f0315 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -217,7 +217,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, &arg.ty); write!(out, "({})", arg.ident); } else if arg.ty == RustString { - write!(out, "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident); + write!( + out, + "::rust::String(::rust::unsafe_bitcopy, *{})", + arg.ident, + ); } else if types.needs_indirect_abi(&arg.ty) { write!(out, "::std::move(*{})", arg.ident); } else { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9ebb623..dd5ab6e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -252,7 +252,9 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type let vars = efn.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { - Type::Ident(i) if i == RustString => quote!(::std::mem::take((*#ident).as_mut_string())), + Type::Ident(i) if i == RustString => { + quote!(::std::mem::take((*#ident).as_mut_string())) + } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { From 9c68b1a0c67cb2830eeca0e5206f97b4a710ae7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 19:27:29 +0000 Subject: [PATCH 101/2232] Lazily compute include set --- diff --git a/gen/include.rs b/gen/include.rs index ef741af..e34d9d0 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display}; + pub static HEADER: &str = include_str!("include/cxxbridge.h"); pub fn get(guard: &str) -> &'static str { @@ -11,3 +13,43 @@ pub fn get(guard: &str) -> &'static str { panic!("not found in cxxbridge.h header: {}", guard) } } + +#[derive(Default)] +pub struct Includes { + custom: Vec, + pub cstdint: bool, + pub memory: bool, + pub string: bool, + pub type_traits: bool, +} + +impl Includes { + pub fn new() -> Self { + Includes::default() + } + + pub fn insert(&mut self, include: String) { + self.custom.push(include); + } +} + +impl Display for Includes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for include in &self.custom { + writeln!(f, "#include \"{}\"", include.escape_default())?; + } + if self.cstdint { + writeln!(f, "#include ")?; + } + if self.memory { + writeln!(f, "#include ")?; + } + if self.string { + writeln!(f, "#include ")?; + } + if self.type_traits { + writeln!(f, "#include ")?; + } + Ok(()) + } +} diff --git a/gen/out.rs b/gen/out.rs index 506ce6c..124816f 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -1,8 +1,10 @@ +use crate::gen::include::Includes; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { pub namespace: Vec, pub header: bool, + pub include: Includes, content: Vec, section_pending: bool, blocks_pending: Vec<&'static str>, @@ -13,6 +15,7 @@ impl OutFile { OutFile { namespace, header, + include: Includes::new(), content: Vec::new(), section_pending: false, blocks_pending: Vec::new(), @@ -37,6 +40,10 @@ impl OutFile { } } + pub fn prepend(&mut self, section: String) { + self.content.splice(..0, section.into_bytes()); + } + pub fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } diff --git a/gen/write.rs b/gen/write.rs index 68f0315..24c9c09 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -14,7 +14,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b for api in apis { if let Api::Include(include) = api { - writeln!(out, "#include \"{}\"", include.value().escape_default()); + out.include.insert(include.value()); } } @@ -74,41 +74,25 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b write_generic_instantiations(out, types); } + out.prepend(out.include.to_string()); + out_file } fn write_includes(out: &mut OutFile, types: &Types) { - let mut has_int = false; - let mut has_box = false; - let mut has_unique_ptr = false; - let mut has_string = false; - for ty in types { match ty { Type::Ident(ident) => match Atom::from(ident) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => has_int = true, - Some(CxxString) => has_string = true, + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => out.include.cstdint = true, + Some(CxxString) => out.include.string = true, Some(Bool) | Some(RustString) | None => {} }, - Type::RustBox(_) => has_box = true, - Type::UniquePtr(_) => has_unique_ptr = true, + Type::RustBox(_) => out.include.type_traits = true, + Type::UniquePtr(_) => out.include.memory = true, _ => {} } } - - if has_int { - writeln!(out, "#include "); - } - if has_unique_ptr { - writeln!(out, "#include "); - } - if has_string { - writeln!(out, "#include "); - } - if has_box { - writeln!(out, "#include "); - } } fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { From 33169bdd595cc3acbcb306bddde9a28b637e8ad2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 21:10:58 +0000 Subject: [PATCH 102/2232] Safe to assume more about the Box representation https://doc.rust-lang.org/std/boxed/index.html: So long as T: Sized, a Box is guaranteed to be represented as a single pointer and is also ABI-compatible with C pointers (i.e. the C type T*). --- diff --git a/gen/write.rs b/gen/write.rs index 24c9c09..2bd6d3d 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -471,24 +471,9 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - "void cxxbridge01$box${}$set_raw(::rust::Box<{}> *ptr, {} *raw) noexcept;", - instance, inner, inner - ); - writeln!( - out, "void cxxbridge01$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); - writeln!( - out, - "const {} *cxxbridge01$box${}$deref(const ::rust::Box<{}> *ptr) noexcept;", - inner, instance, inner, - ); - writeln!( - out, - "{} *cxxbridge01$box${}$deref_mut(::rust::Box<{}> *ptr) noexcept;", - inner, instance, inner, - ); writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); } @@ -507,40 +492,9 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!(out, "template <>"); - writeln!( - out, - "void Box<{}>::set_raw({} *raw) noexcept {{", - inner, inner, - ); - writeln!( - out, - " return cxxbridge01$box${}$set_raw(this, raw);", - instance - ); - writeln!(out, "}}"); - - writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); writeln!(out, " return cxxbridge01$box${}$drop(this);", instance); writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!( - out, - "const {} *Box<{}>::deref() const noexcept {{", - inner, inner, - ); - writeln!(out, " return cxxbridge01$box${}$deref(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "{} *Box<{}>::deref_mut() noexcept {{", inner, inner); - writeln!( - out, - " return cxxbridge01$box${}$deref_mut(this);", - instance - ); - writeln!(out, "}}"); } fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { diff --git a/include/cxxbridge.h b/include/cxxbridge.h index 1ac3133..81bdad5 100644 --- a/include/cxxbridge.h +++ b/include/cxxbridge.h @@ -81,63 +81,60 @@ public: using pointer = typename std::add_pointer::type; Box(const Box &other) : Box(*other) {} - Box(Box &&other) noexcept : repr(other.repr) { other.repr = 0; } + Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } Box(const T &val) { this->uninit(); - ::new (this->deref_mut()) T(val); + ::new (this->ptr) T(val); } Box &operator=(const Box &other) { if (this != &other) { - if (this->repr) { + if (this->ptr) { **this = *other; } else { this->uninit(); - ::new (this->deref_mut()) T(*other); + ::new (this->ptr) T(*other); } } return *this; } Box &operator=(Box &&other) noexcept { - if (this->repr) { + if (this->ptr) { this->drop(); } - this->repr = other.repr; - other.repr = 0; + this->ptr = other.ptr; + other.ptr = nullptr; return *this; } ~Box() noexcept { - if (this->repr) { + if (this->ptr) { this->drop(); } } - const T *operator->() const noexcept { return this->deref(); } - const T &operator*() const noexcept { return *this->deref(); } - T *operator->() noexcept { return this->deref_mut(); } - T &operator*() noexcept { return *this->deref_mut(); } + const T *operator->() const noexcept { return this->ptr; } + const T &operator*() const noexcept { return *this->ptr; } + T *operator->() noexcept { return this->ptr; } + T &operator*() noexcept { return *this->ptr; } // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. static Box from_raw(T *raw) noexcept { Box box; - box.set_raw(raw); + box.ptr = raw; return box; } T *into_raw() noexcept { - T *raw = this->deref_mut(); - this->repr = 0; + T *raw = this->ptr; + this->ptr = nullptr; return raw; } private: Box() noexcept {} void uninit() noexcept; - void set_raw(pointer) noexcept; void drop() noexcept; - const_pointer deref() const noexcept; - pointer deref_mut() noexcept; - uintptr_t repr; + T *ptr; }; #endif // CXXBRIDGE01_RUST_BOX diff --git a/macro/src/expand.rs b/macro/src/expand.rs index dd5ab6e..8519c1e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -311,17 +311,11 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_prefix = format!("cxxbridge01$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); - let link_set_raw = format!("{}set_raw", link_prefix); let link_drop = format!("{}drop", link_prefix); - let link_deref = format!("{}deref", link_prefix); - let link_deref_mut = format!("{}deref_mut", link_prefix); let local_prefix = format_ident!("{}__box_", ident); let local_uninit = format_ident!("{}uninit", local_prefix); - let local_set_raw = format_ident!("{}set_raw", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); - let local_deref = format_ident!("{}deref", local_prefix); - let local_deref_mut = format_ident!("{}deref_mut", local_prefix); let span = ident.span(); quote_spanned! {span=> @@ -336,32 +330,10 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { ); } #[doc(hidden)] - #[export_name = #link_set_raw] - unsafe extern "C" fn #local_set_raw( - this: *mut ::std::boxed::Box<#ident>, - raw: *mut #ident, - ) { - ::std::ptr::write(this, ::std::boxed::Box::from_raw(raw)); - } - #[doc(hidden)] #[export_name = #link_drop] unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) { ::std::ptr::drop_in_place(this); } - #[doc(hidden)] - #[export_name = #link_deref] - unsafe extern "C" fn #local_deref( - this: *const ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>, - ) -> *const ::std::mem::MaybeUninit<#ident> { - &**this - } - #[doc(hidden)] - #[export_name = #link_deref_mut] - unsafe extern "C" fn #local_deref_mut( - this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>, - ) -> *mut ::std::mem::MaybeUninit<#ident> { - &mut **this - } } } From f51447e2b9a0fba982d17abf2401c1f3f0af7525 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 22:22:00 +0000 Subject: [PATCH 103/2232] Implement moving ownership of struct arguments to Rust --- diff --git a/gen/write.rs b/gen/write.rs index 2bd6d3d..187bf5e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -19,7 +19,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b } write_includes(out, types); - write_include_cxxbridge(out, types); + write_include_cxxbridge(out, apis, types); out.next_section(); for name in &namespace { @@ -95,7 +95,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { } } -fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { +fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_box = false; for ty in types { if let Type::RustBox(_) = ty { @@ -104,16 +104,47 @@ fn write_include_cxxbridge(out: &mut OutFile, types: &Types) { } } + let mut needs_manually_drop = false; + 'outer: for api in apis { + if let Api::RustFunction(efn) = api { + for arg in &efn.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + needs_manually_drop = true; + break 'outer; + } + } + } + } + out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge01"); - if needs_rust_box { + + if needs_rust_box || needs_manually_drop { writeln!(out, "// #include \"cxxbridge.h\""); + } + + if needs_rust_box { + out.next_section(); for line in include::get("CXXBRIDGE01_RUST_BOX").lines() { if !line.trim_start().starts_with("//") { writeln!(out, "{}", line); } } } + + if needs_manually_drop { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union ManuallyDrop {{"); + writeln!(out, " T value;"); + writeln!( + out, + " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", + ); + writeln!(out, " ~ManuallyDrop() {{}}"); + writeln!(out, "}};"); + } + out.end_block("namespace cxxbridge01"); out.end_block("namespace rust"); } @@ -274,6 +305,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ";"); } else { writeln!(out, " {{"); + for arg in &efn.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + write!(out, " ::rust::ManuallyDrop<"); + write_type(out, &arg.ty); + writeln!(out, "> {}$(::std::move({0}));", arg.ident); + } + } write!(out, " "); if indirect_return { write!(out, "char return$[sizeof("); @@ -300,10 +338,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { _ => {} } write!(out, "{}", arg.ident); - match arg.ty { + match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), Type::Str(_) => write!(out, ")"), + ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } } From 09011c304b2c1f532b21e68c22443d467d5f56e7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 22:46:46 +0000 Subject: [PATCH 104/2232] Fix alignment of by-value returns from Rust --- diff --git a/gen/write.rs b/gen/write.rs index 187bf5e..f0bcaf9 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -105,12 +105,18 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } let mut needs_manually_drop = false; - 'outer: for api in apis { + let mut needs_maybe_uninit = false; + for api in apis { if let Api::RustFunction(efn) = api { for arg in &efn.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { needs_manually_drop = true; - break 'outer; + break; + } + } + if let Some(ret) = &efn.ret { + if types.needs_indirect_abi(ret) { + needs_maybe_uninit = true; } } } @@ -119,7 +125,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge01"); - if needs_rust_box || needs_manually_drop { + if needs_rust_box || needs_manually_drop || needs_maybe_uninit { writeln!(out, "// #include \"cxxbridge.h\""); } @@ -145,6 +151,16 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } + if needs_maybe_uninit { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union MaybeUninit {{"); + writeln!(out, " T value;"); + writeln!(out, " MaybeUninit() {{}}"); + writeln!(out, " ~MaybeUninit() {{}}"); + writeln!(out, "}};"); + } + out.end_block("namespace cxxbridge01"); out.end_block("namespace rust"); } @@ -314,9 +330,9 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write!(out, " "); if indirect_return { - write!(out, "char return$[sizeof("); + write!(out, "::rust::MaybeUninit<"); write_type(out, efn.ret.as_ref().unwrap()); - writeln!(out, ")];"); + writeln!(out, "> return$;"); write!(out, " "); } else if let Some(ret) = &efn.ret { write!(out, "return "); @@ -350,17 +366,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if !efn.args.is_empty() { write!(out, ", "); } - write!(out, "reinterpret_cast<"); - write_return_type(out, &efn.ret); - write!(out, "*>(return$)"); + write!(out, "&return$.value"); } writeln!(out, ");"); if indirect_return { - write!(out, " return "); - write_type(out, efn.ret.as_ref().unwrap()); - write!(out, "(*reinterpret_cast<"); - write_return_type(out, &efn.ret); - writeln!(out, "*>(return$));"); + writeln!(out, " return ::std::move(return$.value);"); } writeln!(out, "}}"); } From be13d8ad2c42f951f7b4e11dfa91451c8fc99464 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 23:50:24 +0000 Subject: [PATCH 105/2232] Test Box returned from C++ to Rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 098253a..d1b33c2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -13,7 +13,7 @@ pub mod ffi { fn c_return_primitive() -> usize; fn c_return_shared() -> Shared; - //TODO fn c_return_box() -> Box; + fn c_return_box() -> Box; fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_str(shared: &Shared) -> &str; @@ -55,7 +55,7 @@ pub mod ffi { } } -type R = (); +pub type R = usize; fn r_return_primitive() -> usize { 2020 diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index c94e34d..adbf945 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -2,6 +2,7 @@ #include "tests/ffi/lib.rs" extern "C" void cxx_test_suite_set_correct(); +extern "C" tests::R *cxx_test_suite_get_box(); namespace tests { @@ -13,6 +14,10 @@ size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } +rust::Box c_return_box() { + return rust::Box::from_raw(cxx_test_suite_get_box()); +} + std::unique_ptr c_return_unique_ptr() { return std::unique_ptr(new C{2020}); } diff --git a/tests/test.rs b/tests/test.rs index 28e5c3b..eb06481 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -25,6 +25,7 @@ fn test_c_return() { assert_eq!(2020, ffi::c_return_primitive()); assert_eq!(2020, ffi::c_return_shared().z); + assert_eq!(2020, *ffi::c_return_box()); ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!("2020", ffi::c_return_str(&shared)); @@ -45,7 +46,7 @@ fn test_c_take() { check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); - check!(ffi::c_take_box(Box::new(()))); + check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); check!(ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); @@ -69,3 +70,8 @@ fn test_c_call_r() { } check!(cxx_run_test()); } + +#[no_mangle] +extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { + Box::into_raw(Box::new(2020usize)) +} From a7d00e82810c09c65c1d9256aeea363a9c4ed3fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 06 2020 23:53:33 +0000 Subject: [PATCH 106/2232] Test &R passed from Rust to C++ --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d1b33c2..e21a62c 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -24,7 +24,7 @@ pub mod ffi { fn c_take_shared(shared: Shared); fn c_take_box(r: Box); fn c_take_unique_ptr(c: UniquePtr); - //TODO fn c_take_ref_r(r: &R); + fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); fn c_take_rust_string(s: String); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index adbf945..7b77c07 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -3,6 +3,7 @@ extern "C" void cxx_test_suite_set_correct(); extern "C" tests::R *cxx_test_suite_get_box(); +extern "C" bool cxx_test_suite_r_is_correct(const tests::R *); namespace tests { @@ -48,8 +49,9 @@ void c_take_shared(Shared shared) { } void c_take_box(rust::Box r) { - (void)r; - cxx_test_suite_set_correct(); + if (cxx_test_suite_r_is_correct(&*r)) { + cxx_test_suite_set_correct(); + } } void c_take_unique_ptr(std::unique_ptr c) { @@ -58,7 +60,11 @@ void c_take_unique_ptr(std::unique_ptr c) { } } -void c_take_ref_r(const R &r) { (void)r; } +void c_take_ref_r(const R &r) { + if (cxx_test_suite_r_is_correct(&r)) { + cxx_test_suite_set_correct(); + } +} void c_take_ref_c(const C &c) { if (c.get() == 2020) { diff --git a/tests/test.rs b/tests/test.rs index eb06481..a30aefb 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -75,3 +75,8 @@ fn test_c_call_r() { extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { Box::into_raw(Box::new(2020usize)) } + +#[no_mangle] +unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool { + *r == 2020 +} From 5cd8d61f9d01d92d71d254178614340804112fed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 00:18:44 +0000 Subject: [PATCH 107/2232] Implement returning Box from Rust to C++ --- diff --git a/gen/write.rs b/gen/write.rs index f0bcaf9..7dc625e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -336,8 +336,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); } else if let Some(ret) = &efn.ret { write!(out, "return "); - if let Type::Ref(_) = ret { - write!(out, "*"); + match ret { + Type::RustBox(_) => { + write_type(out, ret); + write!(out, "::from_raw("); + } + Type::Ref(_) => write!(out, "*"), + _ => {} } } for name in out.namespace.clone() { @@ -368,7 +373,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write!(out, "&return$.value"); } - writeln!(out, ");"); + write!(out, ")"); + if let Some(ret) = &efn.ret { + if let Type::RustBox(_) = ret { + write!(out, ")"); + } + } + writeln!(out, ";"); if indirect_return { writeln!(out, " return ::std::move(return$.value);"); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e21a62c..4c4f41c 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -36,7 +36,7 @@ pub mod ffi { fn r_return_primitive() -> usize; fn r_return_shared() -> Shared; - //TODO fn r_return_box() -> Box; + fn r_return_box() -> Box; //TODO fn r_return_unique_ptr() -> UniquePtr; fn r_return_ref(shared: &Shared) -> &usize; fn r_return_str(shared: &Shared) -> &str; @@ -65,6 +65,10 @@ fn r_return_shared() -> ffi::Shared { ffi::Shared { z: 2020 } } +fn r_return_box() -> Box { + Box::new(2020) +} + fn r_return_ref(shared: &ffi::Shared) -> &usize { &shared.z } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 7b77c07..3d30ebe 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -102,6 +102,7 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_primitive() == 2020); ASSERT(r_return_shared().z == 2020); + ASSERT(cxx_test_suite_r_is_correct(&*r_return_box())); ASSERT(r_return_ref(Shared{2020}) == 2020); ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); From 4b3a66edf1eb1d2b4fa648048969a3916e133fa7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 00:22:14 +0000 Subject: [PATCH 108/2232] Implement returning unique_ptr from Rust to C++ --- diff --git a/gen/write.rs b/gen/write.rs index 7dc625e..601f7ef 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -341,6 +341,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, ret); write!(out, "::from_raw("); } + Type::UniquePtr(_) => { + write_type(out, ret); + write!(out, "("); + } Type::Ref(_) => write!(out, "*"), _ => {} } @@ -375,7 +379,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write!(out, ")"); if let Some(ret) = &efn.ret { - if let Type::RustBox(_) = ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { write!(out, ")"); } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4c4f41c..2df3337 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -37,7 +37,7 @@ pub mod ffi { fn r_return_primitive() -> usize; fn r_return_shared() -> Shared; fn r_return_box() -> Box; - //TODO fn r_return_unique_ptr() -> UniquePtr; + fn r_return_unique_ptr() -> UniquePtr; fn r_return_ref(shared: &Shared) -> &usize; fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; @@ -69,6 +69,13 @@ fn r_return_box() -> Box { Box::new(2020) } +fn r_return_unique_ptr() -> UniquePtr { + extern "C" { + fn cxx_test_suite_get_unique_ptr() -> *mut ffi::C; + } + unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) } +} + fn r_return_ref(shared: &ffi::Shared) -> &usize { &shared.z } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 3d30ebe..245cfbe 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -90,6 +90,10 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +extern "C" C *cxx_test_suite_get_unique_ptr() { + return std::unique_ptr(new C{2020}).release(); +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -103,6 +107,7 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_primitive() == 2020); ASSERT(r_return_shared().z == 2020); ASSERT(cxx_test_suite_r_is_correct(&*r_return_box())); + ASSERT(r_return_unique_ptr()->get() == 2020); ASSERT(r_return_ref(Shared{2020}) == 2020); ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); From 2fe58c670c2307518751f55df9531c0eb8832ef3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 00:23:09 +0000 Subject: [PATCH 109/2232] Declare test suite helpers noexcept --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 245cfbe..c7d3c87 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,9 +1,9 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" -extern "C" void cxx_test_suite_set_correct(); -extern "C" tests::R *cxx_test_suite_get_box(); -extern "C" bool cxx_test_suite_r_is_correct(const tests::R *); +extern "C" void cxx_test_suite_set_correct() noexcept; +extern "C" tests::R *cxx_test_suite_get_box() noexcept; +extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { @@ -90,7 +90,7 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } -extern "C" C *cxx_test_suite_get_unique_ptr() { +extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } From 85db2486255239a1c35beaf8047a65140758ec35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 00:26:46 +0000 Subject: [PATCH 110/2232] Test returning unique_ptr from Rust to C++ --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 2df3337..c709678 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -41,7 +41,7 @@ pub mod ffi { fn r_return_ref(shared: &Shared) -> &usize; fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; - //TODO fn r_return_unique_ptr_string() -> UniquePtr; + fn r_return_unique_ptr_string() -> UniquePtr; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -89,6 +89,13 @@ fn r_return_rust_string() -> String { "2020".to_owned() } +fn r_return_unique_ptr_string() -> UniquePtr { + extern "C" { + fn cxx_test_suite_get_unique_ptr_string() -> *mut CxxString; + } + unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) } +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index c7d3c87..350cd7d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -94,6 +94,10 @@ extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } +extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept { + return std::unique_ptr(new std::string("2020")).release(); +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -111,6 +115,7 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_ref(Shared{2020}) == 2020); ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); + ASSERT(*r_return_unique_ptr_string() == "2020"); r_take_primitive(2020); r_take_shared(Shared{2020}); From 68e5e26e81d69c98279fcb6fbbac5158f97ed399 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 19:16:11 +0000 Subject: [PATCH 111/2232] Exclude some unneeded directories from published crate --- diff --git a/Cargo.toml b/Cargo.toml index fdfadc8..886e01b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" documentation = "https://docs.rs/cxx" readme = "README.md" +exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] [badges] travis-ci = { repository = "dtolnay/cxx" } From 5e93c89e32b4dd1f2e62cea290df4b3075d8b0b0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 19:18:11 +0000 Subject: [PATCH 112/2232] Link license files into subcrate packages --- diff --git a/cmd/LICENSE-APACHE b/cmd/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/cmd/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/cmd/LICENSE-MIT b/cmd/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/cmd/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/macro/LICENSE-APACHE b/macro/LICENSE-APACHE new file mode 120000 index 0000000..965b606 --- /dev/null +++ b/macro/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/macro/LICENSE-MIT b/macro/LICENSE-MIT new file mode 120000 index 0000000..76219eb --- /dev/null +++ b/macro/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file From f370ea46c7487051f23cc97e6b76b41c643c93d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 07 2020 19:48:52 +0000 Subject: [PATCH 113/2232] Force cargo metadata to include dependencies on cmd --- diff --git a/cmd/src/lib.rs b/cmd/src/lib.rs new file mode 100644 index 0000000..8b1a393 --- /dev/null +++ b/cmd/src/lib.rs @@ -0,0 +1 @@ +// empty From eb87f6541003447aa627f7cc1179ec883cf2adf9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 08 2020 03:47:50 +0000 Subject: [PATCH 114/2232] Update to bazelbuild/rules_rust master branch --- diff --git a/WORKSPACE b/WORKSPACE index 2feb345..3e3baed 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "b7ac870f4cab1cd7e56fd2cbe303f63d78d21cc1a6e3922f21887d373c090e20", - strip_prefix = "rules_rust-5a679d418955a122798f42c7bb67c55ca68a2493", - # Master branch as of 2020-02-24 - url = "https://github.com/dtolnay/rules_rust/archive/5a679d418955a122798f42c7bb67c55ca68a2493.tar.gz", + sha256 = "abc75a5b6c8eda46a3d141921841e3577e9707b32d4d5b5cc156f7b8b28631ad", + strip_prefix = "rules_rust-d97f99628439df8bec89f5b7bc439f9d43d1586b", + # Master branch as of 2020-03-07 + url = "https://github.com/bazelbuild/rules_rust/archive/d97f99628439df8bec89f5b7bc439f9d43d1586b.tar.gz", ) http_archive( From 736cbcad40735bc6a64a4fbe1609f8c372c93746 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 11 2020 23:51:38 +0000 Subject: [PATCH 115/2232] Move header include path to rust/cxx.h --- diff --git a/BUCK b/BUCK index 2d12753..2f80c9b 100644 --- a/BUCK +++ b/BUCK @@ -38,11 +38,11 @@ rust_binary( cxx_library( name = "core", - srcs = ["src/cxxbridge.cc"], + srcs = ["src/cxx.cc"], visibility = ["PUBLIC"], - header_namespace = "cxxbridge", + header_namespace = "rust", exported_headers = { - "cxxbridge.h": "include/cxxbridge.h", + "cxx.h": "include/cxx.h", }, exported_linker_flags = ["-lstdc++"], ) diff --git a/BUILD b/BUILD index 0405df1..db22203 100644 --- a/BUILD +++ b/BUILD @@ -3,7 +3,7 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), - data = ["src/gen/include/cxxbridge.h"], + data = ["src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ ":core-lib", @@ -23,7 +23,7 @@ rust_library( rust_binary( name = "codegen", srcs = glob(["cmd/src/**/*.rs"]), - data = ["cmd/src/gen/include/cxxbridge.h"], + data = ["cmd/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", @@ -39,16 +39,16 @@ rust_binary( cc_library( name = "core", - hdrs = ["include/cxxbridge.h"], - include_prefix = "cxxbridge", + hdrs = ["include/cxx.h"], + include_prefix = "rust", strip_include_prefix = "include", visibility = ["//visibility:public"], ) cc_library( name = "core-lib", - srcs = ["src/cxxbridge.cc"], - hdrs = ["include/cxxbridge.h"], + srcs = ["src/cxx.cc"], + hdrs = ["include/cxx.h"], ) rust_library( diff --git a/README.md b/README.md index 07ea91e..2886082 100644 --- a/README.md +++ b/README.md @@ -306,9 +306,9 @@ of functions. -The C++ API of the `cxxbridge` namespace is defined by the *include/cxxbridge.h* -file in this repo. You will need to include this header in your C++ code when -working with those types. +The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in +this repo. You will need to include this header in your C++ code when working +with those types. The following types are intended to be supported "soon" but are just not implemented yet. I don't expect any of these to be hard to make work but it's a diff --git a/build.rs b/build.rs index ea562c2..bdda779 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,8 @@ fn main() { cc::Build::new() - .file("src/cxxbridge.cc") + .file("src/cxx.cc") .flag("-std=c++11") .compile("cxxbridge01"); - println!("cargo:rerun-if-changed=src/cxxbridge.cc"); - println!("cargo:rerun-if-changed=include/cxxbridge.h"); + println!("cargo:rerun-if-changed=src/cxx.cc"); + println!("cargo:rerun-if-changed=include/cxx.h"); } diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 26b6ef6..06b3bbe 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -14,7 +14,7 @@ use structopt::StructOpt; usage = "\ cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit cxxbridge.h header to stdout", + cxxbridge --header Emit rust/cxx.h header to stdout", help_message = "Print help information", version_message = "Print version information" )] diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index a579986..fafc474 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -1,5 +1,5 @@ #pragma once -#include "cxxbridge/cxxbridge.h" +#include "rust/cxx.h" #include #include diff --git a/gen/include.rs b/gen/include.rs index e34d9d0..a4b416a 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -pub static HEADER: &str = include_str!("include/cxxbridge.h"); +pub static HEADER: &str = include_str!("include/cxx.h"); pub fn get(guard: &str) -> &'static str { let ifndef = format!("#ifndef {}", guard); @@ -10,7 +10,7 @@ pub fn get(guard: &str) -> &'static str { if let (Some(begin), Some(end)) = (begin, end) { &HEADER[begin..end + endif.len()] } else { - panic!("not found in cxxbridge.h header: {}", guard) + panic!("not found in cxx.h header: {}", guard) } } diff --git a/gen/write.rs b/gen/write.rs index 601f7ef..9ab98fa 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -126,7 +126,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.begin_block("inline namespace cxxbridge01"); if needs_rust_box || needs_manually_drop || needs_maybe_uninit { - writeln!(out, "// #include \"cxxbridge.h\""); + writeln!(out, "// #include \"rust/cxx.h\""); } if needs_rust_box { diff --git a/include/cxx.h b/include/cxx.h new file mode 100644 index 0000000..81bdad5 --- /dev/null +++ b/include/cxx.h @@ -0,0 +1,155 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace rust { +inline namespace cxxbridge01 { + +struct unsafe_bitcopy_t; + +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + + String &operator=(const String &) noexcept; + String &operator=(String &&) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + + // Internal API only intended for the cxxbridge code generator. + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + // Size and alignment statically verified by rust_string.rs. + std::array repr; +}; + +class Str final { +public: + Str() noexcept; + Str(const Str &) noexcept; + + Str(const std::string &); + Str(const char *); + Str(std::string &&) = delete; + + Str &operator=(Str) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // Not necessarily ABI compatible with &str. Codegen will translate to + // cxx::rust_str::RustStr which matches this layout. + struct Repr { + const char *ptr; + size_t len; + }; + Str(Repr) noexcept; + explicit operator Repr() noexcept; + +private: + Repr repr; +}; + +#ifndef CXXBRIDGE01_RUST_BOX +#define CXXBRIDGE01_RUST_BOX +template class Box final { +public: + using value_type = T; + using const_pointer = typename std::add_pointer< + typename std::add_const::type>::type; + using pointer = typename std::add_pointer::type; + + Box(const Box &other) : Box(*other) {} + Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } + Box(const T &val) { + this->uninit(); + ::new (this->ptr) T(val); + } + Box &operator=(const Box &other) { + if (this != &other) { + if (this->ptr) { + **this = *other; + } else { + this->uninit(); + ::new (this->ptr) T(*other); + } + } + return *this; + } + Box &operator=(Box &&other) noexcept { + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; + } + ~Box() noexcept { + if (this->ptr) { + this->drop(); + } + } + + const T *operator->() const noexcept { return this->ptr; } + const T &operator*() const noexcept { return *this->ptr; } + T *operator->() noexcept { return this->ptr; } + T &operator*() noexcept { return *this->ptr; } + + // Important: requires that `raw` came from an into_raw call. Do not pass a + // pointer from `new` or any other source. + static Box from_raw(T *raw) noexcept { + Box box; + box.ptr = raw; + return box; + } + + T *into_raw() noexcept { + T *raw = this->ptr; + this->ptr = nullptr; + return raw; + } + +private: + Box() noexcept {} + void uninit() noexcept; + void drop() noexcept; + T *ptr; +}; +#endif // CXXBRIDGE01_RUST_BOX + +std::ostream &operator<<(std::ostream &, const String &); +std::ostream &operator<<(std::ostream &, const Str &); + +// Snake case aliases for use in code that uses this style for type names. +using string = String; +using str = Str; +template using box = Box; + +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; + +} // namespace cxxbridge01 +} // namespace rust diff --git a/include/cxxbridge.h b/include/cxxbridge.h deleted file mode 100644 index 81bdad5..0000000 --- a/include/cxxbridge.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace rust { -inline namespace cxxbridge01 { - -struct unsafe_bitcopy_t; - -class String final { -public: - String() noexcept; - String(const String &) noexcept; - String(String &&) noexcept; - ~String() noexcept; - - String(const std::string &); - String(const char *); - - String &operator=(const String &) noexcept; - String &operator=(String &&) noexcept; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - size_t size() const noexcept; - size_t length() const noexcept; - - // Internal API only intended for the cxxbridge code generator. - String(unsafe_bitcopy_t, const String &) noexcept; - -private: - // Size and alignment statically verified by rust_string.rs. - std::array repr; -}; - -class Str final { -public: - Str() noexcept; - Str(const Str &) noexcept; - - Str(const std::string &); - Str(const char *); - Str(std::string &&) = delete; - - Str &operator=(Str) noexcept; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - size_t size() const noexcept; - size_t length() const noexcept; - - // Repr is PRIVATE; must not be used other than by our generated code. - // - // Not necessarily ABI compatible with &str. Codegen will translate to - // cxx::rust_str::RustStr which matches this layout. - struct Repr { - const char *ptr; - size_t len; - }; - Str(Repr) noexcept; - explicit operator Repr() noexcept; - -private: - Repr repr; -}; - -#ifndef CXXBRIDGE01_RUST_BOX -#define CXXBRIDGE01_RUST_BOX -template class Box final { -public: - using value_type = T; - using const_pointer = typename std::add_pointer< - typename std::add_const::type>::type; - using pointer = typename std::add_pointer::type; - - Box(const Box &other) : Box(*other) {} - Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - Box(const T &val) { - this->uninit(); - ::new (this->ptr) T(val); - } - Box &operator=(const Box &other) { - if (this != &other) { - if (this->ptr) { - **this = *other; - } else { - this->uninit(); - ::new (this->ptr) T(*other); - } - } - return *this; - } - Box &operator=(Box &&other) noexcept { - if (this->ptr) { - this->drop(); - } - this->ptr = other.ptr; - other.ptr = nullptr; - return *this; - } - ~Box() noexcept { - if (this->ptr) { - this->drop(); - } - } - - const T *operator->() const noexcept { return this->ptr; } - const T &operator*() const noexcept { return *this->ptr; } - T *operator->() noexcept { return this->ptr; } - T &operator*() noexcept { return *this->ptr; } - - // Important: requires that `raw` came from an into_raw call. Do not pass a - // pointer from `new` or any other source. - static Box from_raw(T *raw) noexcept { - Box box; - box.ptr = raw; - return box; - } - - T *into_raw() noexcept { - T *raw = this->ptr; - this->ptr = nullptr; - return raw; - } - -private: - Box() noexcept {} - void uninit() noexcept; - void drop() noexcept; - T *ptr; -}; -#endif // CXXBRIDGE01_RUST_BOX - -std::ostream &operator<<(std::ostream &, const String &); -std::ostream &operator<<(std::ostream &, const Str &); - -// Snake case aliases for use in code that uses this style for type names. -using string = String; -using str = Str; -template using box = Box; - -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; - -} // namespace cxxbridge01 -} // namespace rust diff --git a/src/cxx.cc b/src/cxx.cc new file mode 100644 index 0000000..f7b02ec --- /dev/null +++ b/src/cxx.cc @@ -0,0 +1,166 @@ +#include "../include/cxx.h" +#include +#include +#include +#include + +extern "C" { +const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { + return s.data(); +} + +size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { + return s.length(); +} + +// rust::String +void cxxbridge01$string$new(rust::String *self) noexcept; +void cxxbridge01$string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge01$string$from(rust::String *self, const char *ptr, + size_t len) noexcept; +void cxxbridge01$string$drop(rust::String *self) noexcept; +const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge01$string$len(const rust::String *self) noexcept; + +// rust::Str +bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; +} // extern "C" + +namespace rust { +inline namespace cxxbridge01 { + +String::String() noexcept { cxxbridge01$string$new(this); } + +String::String(const String &other) noexcept { + cxxbridge01$string$clone(this, other); +} + +String::String(String &&other) noexcept { + this->repr = other.repr; + cxxbridge01$string$new(&other); +} + +String::~String() noexcept { cxxbridge01$string$drop(this); } + +String::String(const std::string &s) { + auto ptr = s.data(); + auto len = s.length(); + if (!cxxbridge01$string$from(this, ptr, len)) { + throw std::invalid_argument("data for rust::String is not utf-8"); + } +} + +String::String(const char *s) { + auto len = strlen(s); + if (!cxxbridge01$string$from(this, s, len)) { + throw std::invalid_argument("data for rust::String is not utf-8"); + } +} + +String &String::operator=(const String &other) noexcept { + if (this != &other) { + cxxbridge01$string$drop(this); + cxxbridge01$string$clone(this, other); + } + return *this; +} + +String &String::operator=(String &&other) noexcept { + if (this != &other) { + cxxbridge01$string$drop(this); + this->repr = other.repr; + cxxbridge01$string$new(&other); + } + return *this; +} + +String::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *String::data() const noexcept { + return cxxbridge01$string$ptr(this); +} + +size_t String::size() const noexcept { return cxxbridge01$string$len(this); } + +size_t String::length() const noexcept { return cxxbridge01$string$len(this); } + +String::String(unsafe_bitcopy_t, const String &bits) noexcept + : repr(bits.repr) {} + +std::ostream &operator<<(std::ostream &os, const String &s) { + os.write(s.data(), s.size()); + return os; +} + +Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + +Str::Str(const Str &) noexcept = default; + +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + throw std::invalid_argument("data for rust::Str is not utf-8"); + } +} + +Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + throw std::invalid_argument("data for rust::Str is not utf-8"); + } +} + +Str &Str::operator=(Str other) noexcept { + this->repr = other.repr; + return *this; +} + +Str::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *Str::data() const noexcept { return this->repr.ptr; } + +size_t Str::size() const noexcept { return this->repr.len; } + +size_t Str::length() const noexcept { return this->repr.len; } + +Str::Str(Repr repr_) noexcept : repr(repr_) {} + +Str::operator Repr() noexcept { return this->repr; } + +std::ostream &operator<<(std::ostream &os, const Str &s) { + os.write(s.data(), s.size()); + return os; +} + +} // namespace cxxbridge01 +} // namespace rust + +extern "C" { +void cxxbridge01$unique_ptr$std$string$null( + std::unique_ptr *ptr) noexcept { + new (ptr) std::unique_ptr(); +} +void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, + std::string *value) noexcept { + new (ptr) std::unique_ptr(new std::string(std::move(*value))); +} +void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, + std::string *raw) noexcept { + new (ptr) std::unique_ptr(raw); +} +const std::string *cxxbridge01$unique_ptr$std$string$get( + const std::unique_ptr &ptr) noexcept { + return ptr.get(); +} +std::string *cxxbridge01$unique_ptr$std$string$release( + std::unique_ptr &ptr) noexcept { + return ptr.release(); +} +void cxxbridge01$unique_ptr$std$string$drop( + std::unique_ptr *ptr) noexcept { + ptr->~unique_ptr(); +} +} // extern "C" diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc deleted file mode 100644 index b44204d..0000000 --- a/src/cxxbridge.cc +++ /dev/null @@ -1,166 +0,0 @@ -#include "../include/cxxbridge.h" -#include -#include -#include -#include - -extern "C" { -const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { - return s.data(); -} - -size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { - return s.length(); -} - -// rust::String -void cxxbridge01$string$new(rust::String *self) noexcept; -void cxxbridge01$string$clone(rust::String *self, - const rust::String &other) noexcept; -bool cxxbridge01$string$from(rust::String *self, const char *ptr, - size_t len) noexcept; -void cxxbridge01$string$drop(rust::String *self) noexcept; -const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge01$string$len(const rust::String *self) noexcept; - -// rust::Str -bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; -} // extern "C" - -namespace rust { -inline namespace cxxbridge01 { - -String::String() noexcept { cxxbridge01$string$new(this); } - -String::String(const String &other) noexcept { - cxxbridge01$string$clone(this, other); -} - -String::String(String &&other) noexcept { - this->repr = other.repr; - cxxbridge01$string$new(&other); -} - -String::~String() noexcept { cxxbridge01$string$drop(this); } - -String::String(const std::string &s) { - auto ptr = s.data(); - auto len = s.length(); - if (!cxxbridge01$string$from(this, ptr, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); - } -} - -String::String(const char *s) { - auto len = strlen(s); - if (!cxxbridge01$string$from(this, s, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); - } -} - -String &String::operator=(const String &other) noexcept { - if (this != &other) { - cxxbridge01$string$drop(this); - cxxbridge01$string$clone(this, other); - } - return *this; -} - -String &String::operator=(String &&other) noexcept { - if (this != &other) { - cxxbridge01$string$drop(this); - this->repr = other.repr; - cxxbridge01$string$new(&other); - } - return *this; -} - -String::operator std::string() const { - return std::string(this->data(), this->size()); -} - -const char *String::data() const noexcept { - return cxxbridge01$string$ptr(this); -} - -size_t String::size() const noexcept { return cxxbridge01$string$len(this); } - -size_t String::length() const noexcept { return cxxbridge01$string$len(this); } - -String::String(unsafe_bitcopy_t, const String &bits) noexcept - : repr(bits.repr) {} - -std::ostream &operator<<(std::ostream &os, const String &s) { - os.write(s.data(), s.size()); - return os; -} - -Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} - -Str::Str(const Str &) noexcept = default; - -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); - } -} - -Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); - } -} - -Str &Str::operator=(Str other) noexcept { - this->repr = other.repr; - return *this; -} - -Str::operator std::string() const { - return std::string(this->data(), this->size()); -} - -const char *Str::data() const noexcept { return this->repr.ptr; } - -size_t Str::size() const noexcept { return this->repr.len; } - -size_t Str::length() const noexcept { return this->repr.len; } - -Str::Str(Repr repr_) noexcept : repr(repr_) {} - -Str::operator Repr() noexcept { return this->repr; } - -std::ostream &operator<<(std::ostream &os, const Str &s) { - os.write(s.data(), s.size()); - return os; -} - -} // namespace cxxbridge01 -} // namespace rust - -extern "C" { -void cxxbridge01$unique_ptr$std$string$null( - std::unique_ptr *ptr) noexcept { - new (ptr) std::unique_ptr(); -} -void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, - std::string *value) noexcept { - new (ptr) std::unique_ptr(new std::string(std::move(*value))); -} -void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, - std::string *raw) noexcept { - new (ptr) std::unique_ptr(raw); -} -const std::string *cxxbridge01$unique_ptr$std$string$get( - const std::unique_ptr &ptr) noexcept { - return ptr.get(); -} -std::string *cxxbridge01$unique_ptr$std$string$release( - std::unique_ptr &ptr) noexcept { - return ptr.release(); -} -void cxxbridge01$unique_ptr$std$string$drop( - std::unique_ptr *ptr) noexcept { - ptr->~unique_ptr(); -} -} // extern "C" diff --git a/src/lib.rs b/src/lib.rs index 006159a..316918f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,9 +310,9 @@ //! //! //! -//! The C++ API of the `cxxbridge` namespace is defined by the -//! *include/cxxbridge.h* file in https://github.com/dtolnay/cxx. You will need -//! to include this header in your C++ code when working with those types. +//! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file +//! in https://github.com/dtolnay/cxx. You will need to include this header in +//! your C++ code when working with those types. //! //! The following types are intended to be supported "soon" but are just not //! implemented yet. I don't expect any of these to be hard to make work but @@ -463,10 +463,10 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let mut build = paths::cc_build(); build.file(&bridge_path); - let ref cxxbridge_h = paths::include_dir()?.join("cxxbridge").join("cxxbridge.h"); - let _ = fs::create_dir_all(cxxbridge_h.parent().unwrap()); - let _ = fs::remove_file(cxxbridge_h); - let _ = fs::write(cxxbridge_h, gen::include::HEADER); + let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); + let _ = fs::create_dir_all(cxx_h.parent().unwrap()); + let _ = fs::remove_file(cxx_h); + let _ = fs::write(cxx_h, gen::include::HEADER); Ok(build) } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f41cc5e..7cff6fa 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -1,5 +1,5 @@ #pragma once -#include "cxxbridge/cxxbridge.h" +#include "rust/cxx.h" #include #include From 77c18a0f731d0cd9c8d424839e78cba39edfdde1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 12 2020 00:04:31 +0000 Subject: [PATCH 116/2232] Merge pull request #62 from dtolnay/include Move header include path to rust/cxx.h --- diff --git a/BUCK b/BUCK index 2d12753..2f80c9b 100644 --- a/BUCK +++ b/BUCK @@ -38,11 +38,11 @@ rust_binary( cxx_library( name = "core", - srcs = ["src/cxxbridge.cc"], + srcs = ["src/cxx.cc"], visibility = ["PUBLIC"], - header_namespace = "cxxbridge", + header_namespace = "rust", exported_headers = { - "cxxbridge.h": "include/cxxbridge.h", + "cxx.h": "include/cxx.h", }, exported_linker_flags = ["-lstdc++"], ) diff --git a/BUILD b/BUILD index 0405df1..db22203 100644 --- a/BUILD +++ b/BUILD @@ -3,7 +3,7 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), - data = ["src/gen/include/cxxbridge.h"], + data = ["src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ ":core-lib", @@ -23,7 +23,7 @@ rust_library( rust_binary( name = "codegen", srcs = glob(["cmd/src/**/*.rs"]), - data = ["cmd/src/gen/include/cxxbridge.h"], + data = ["cmd/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", @@ -39,16 +39,16 @@ rust_binary( cc_library( name = "core", - hdrs = ["include/cxxbridge.h"], - include_prefix = "cxxbridge", + hdrs = ["include/cxx.h"], + include_prefix = "rust", strip_include_prefix = "include", visibility = ["//visibility:public"], ) cc_library( name = "core-lib", - srcs = ["src/cxxbridge.cc"], - hdrs = ["include/cxxbridge.h"], + srcs = ["src/cxx.cc"], + hdrs = ["include/cxx.h"], ) rust_library( diff --git a/README.md b/README.md index 07ea91e..2886082 100644 --- a/README.md +++ b/README.md @@ -306,9 +306,9 @@ of functions. -The C++ API of the `cxxbridge` namespace is defined by the *include/cxxbridge.h* -file in this repo. You will need to include this header in your C++ code when -working with those types. +The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in +this repo. You will need to include this header in your C++ code when working +with those types. The following types are intended to be supported "soon" but are just not implemented yet. I don't expect any of these to be hard to make work but it's a diff --git a/build.rs b/build.rs index ea562c2..bdda779 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,8 @@ fn main() { cc::Build::new() - .file("src/cxxbridge.cc") + .file("src/cxx.cc") .flag("-std=c++11") .compile("cxxbridge01"); - println!("cargo:rerun-if-changed=src/cxxbridge.cc"); - println!("cargo:rerun-if-changed=include/cxxbridge.h"); + println!("cargo:rerun-if-changed=src/cxx.cc"); + println!("cargo:rerun-if-changed=include/cxx.h"); } diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 26b6ef6..06b3bbe 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -14,7 +14,7 @@ use structopt::StructOpt; usage = "\ cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit cxxbridge.h header to stdout", + cxxbridge --header Emit rust/cxx.h header to stdout", help_message = "Print help information", version_message = "Print version information" )] diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index a579986..fafc474 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -1,5 +1,5 @@ #pragma once -#include "cxxbridge/cxxbridge.h" +#include "rust/cxx.h" #include #include diff --git a/gen/include.rs b/gen/include.rs index e34d9d0..a4b416a 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -pub static HEADER: &str = include_str!("include/cxxbridge.h"); +pub static HEADER: &str = include_str!("include/cxx.h"); pub fn get(guard: &str) -> &'static str { let ifndef = format!("#ifndef {}", guard); @@ -10,7 +10,7 @@ pub fn get(guard: &str) -> &'static str { if let (Some(begin), Some(end)) = (begin, end) { &HEADER[begin..end + endif.len()] } else { - panic!("not found in cxxbridge.h header: {}", guard) + panic!("not found in cxx.h header: {}", guard) } } diff --git a/gen/write.rs b/gen/write.rs index 601f7ef..9ab98fa 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -126,7 +126,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.begin_block("inline namespace cxxbridge01"); if needs_rust_box || needs_manually_drop || needs_maybe_uninit { - writeln!(out, "// #include \"cxxbridge.h\""); + writeln!(out, "// #include \"rust/cxx.h\""); } if needs_rust_box { diff --git a/include/cxx.h b/include/cxx.h new file mode 100644 index 0000000..81bdad5 --- /dev/null +++ b/include/cxx.h @@ -0,0 +1,155 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace rust { +inline namespace cxxbridge01 { + +struct unsafe_bitcopy_t; + +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + + String &operator=(const String &) noexcept; + String &operator=(String &&) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + + // Internal API only intended for the cxxbridge code generator. + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + // Size and alignment statically verified by rust_string.rs. + std::array repr; +}; + +class Str final { +public: + Str() noexcept; + Str(const Str &) noexcept; + + Str(const std::string &); + Str(const char *); + Str(std::string &&) = delete; + + Str &operator=(Str) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // Not necessarily ABI compatible with &str. Codegen will translate to + // cxx::rust_str::RustStr which matches this layout. + struct Repr { + const char *ptr; + size_t len; + }; + Str(Repr) noexcept; + explicit operator Repr() noexcept; + +private: + Repr repr; +}; + +#ifndef CXXBRIDGE01_RUST_BOX +#define CXXBRIDGE01_RUST_BOX +template class Box final { +public: + using value_type = T; + using const_pointer = typename std::add_pointer< + typename std::add_const::type>::type; + using pointer = typename std::add_pointer::type; + + Box(const Box &other) : Box(*other) {} + Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } + Box(const T &val) { + this->uninit(); + ::new (this->ptr) T(val); + } + Box &operator=(const Box &other) { + if (this != &other) { + if (this->ptr) { + **this = *other; + } else { + this->uninit(); + ::new (this->ptr) T(*other); + } + } + return *this; + } + Box &operator=(Box &&other) noexcept { + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; + } + ~Box() noexcept { + if (this->ptr) { + this->drop(); + } + } + + const T *operator->() const noexcept { return this->ptr; } + const T &operator*() const noexcept { return *this->ptr; } + T *operator->() noexcept { return this->ptr; } + T &operator*() noexcept { return *this->ptr; } + + // Important: requires that `raw` came from an into_raw call. Do not pass a + // pointer from `new` or any other source. + static Box from_raw(T *raw) noexcept { + Box box; + box.ptr = raw; + return box; + } + + T *into_raw() noexcept { + T *raw = this->ptr; + this->ptr = nullptr; + return raw; + } + +private: + Box() noexcept {} + void uninit() noexcept; + void drop() noexcept; + T *ptr; +}; +#endif // CXXBRIDGE01_RUST_BOX + +std::ostream &operator<<(std::ostream &, const String &); +std::ostream &operator<<(std::ostream &, const Str &); + +// Snake case aliases for use in code that uses this style for type names. +using string = String; +using str = Str; +template using box = Box; + +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; + +} // namespace cxxbridge01 +} // namespace rust diff --git a/include/cxxbridge.h b/include/cxxbridge.h deleted file mode 100644 index 81bdad5..0000000 --- a/include/cxxbridge.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace rust { -inline namespace cxxbridge01 { - -struct unsafe_bitcopy_t; - -class String final { -public: - String() noexcept; - String(const String &) noexcept; - String(String &&) noexcept; - ~String() noexcept; - - String(const std::string &); - String(const char *); - - String &operator=(const String &) noexcept; - String &operator=(String &&) noexcept; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - size_t size() const noexcept; - size_t length() const noexcept; - - // Internal API only intended for the cxxbridge code generator. - String(unsafe_bitcopy_t, const String &) noexcept; - -private: - // Size and alignment statically verified by rust_string.rs. - std::array repr; -}; - -class Str final { -public: - Str() noexcept; - Str(const Str &) noexcept; - - Str(const std::string &); - Str(const char *); - Str(std::string &&) = delete; - - Str &operator=(Str) noexcept; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - size_t size() const noexcept; - size_t length() const noexcept; - - // Repr is PRIVATE; must not be used other than by our generated code. - // - // Not necessarily ABI compatible with &str. Codegen will translate to - // cxx::rust_str::RustStr which matches this layout. - struct Repr { - const char *ptr; - size_t len; - }; - Str(Repr) noexcept; - explicit operator Repr() noexcept; - -private: - Repr repr; -}; - -#ifndef CXXBRIDGE01_RUST_BOX -#define CXXBRIDGE01_RUST_BOX -template class Box final { -public: - using value_type = T; - using const_pointer = typename std::add_pointer< - typename std::add_const::type>::type; - using pointer = typename std::add_pointer::type; - - Box(const Box &other) : Box(*other) {} - Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - Box(const T &val) { - this->uninit(); - ::new (this->ptr) T(val); - } - Box &operator=(const Box &other) { - if (this != &other) { - if (this->ptr) { - **this = *other; - } else { - this->uninit(); - ::new (this->ptr) T(*other); - } - } - return *this; - } - Box &operator=(Box &&other) noexcept { - if (this->ptr) { - this->drop(); - } - this->ptr = other.ptr; - other.ptr = nullptr; - return *this; - } - ~Box() noexcept { - if (this->ptr) { - this->drop(); - } - } - - const T *operator->() const noexcept { return this->ptr; } - const T &operator*() const noexcept { return *this->ptr; } - T *operator->() noexcept { return this->ptr; } - T &operator*() noexcept { return *this->ptr; } - - // Important: requires that `raw` came from an into_raw call. Do not pass a - // pointer from `new` or any other source. - static Box from_raw(T *raw) noexcept { - Box box; - box.ptr = raw; - return box; - } - - T *into_raw() noexcept { - T *raw = this->ptr; - this->ptr = nullptr; - return raw; - } - -private: - Box() noexcept {} - void uninit() noexcept; - void drop() noexcept; - T *ptr; -}; -#endif // CXXBRIDGE01_RUST_BOX - -std::ostream &operator<<(std::ostream &, const String &); -std::ostream &operator<<(std::ostream &, const Str &); - -// Snake case aliases for use in code that uses this style for type names. -using string = String; -using str = Str; -template using box = Box; - -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; - -} // namespace cxxbridge01 -} // namespace rust diff --git a/src/cxx.cc b/src/cxx.cc new file mode 100644 index 0000000..f7b02ec --- /dev/null +++ b/src/cxx.cc @@ -0,0 +1,166 @@ +#include "../include/cxx.h" +#include +#include +#include +#include + +extern "C" { +const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { + return s.data(); +} + +size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { + return s.length(); +} + +// rust::String +void cxxbridge01$string$new(rust::String *self) noexcept; +void cxxbridge01$string$clone(rust::String *self, + const rust::String &other) noexcept; +bool cxxbridge01$string$from(rust::String *self, const char *ptr, + size_t len) noexcept; +void cxxbridge01$string$drop(rust::String *self) noexcept; +const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge01$string$len(const rust::String *self) noexcept; + +// rust::Str +bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; +} // extern "C" + +namespace rust { +inline namespace cxxbridge01 { + +String::String() noexcept { cxxbridge01$string$new(this); } + +String::String(const String &other) noexcept { + cxxbridge01$string$clone(this, other); +} + +String::String(String &&other) noexcept { + this->repr = other.repr; + cxxbridge01$string$new(&other); +} + +String::~String() noexcept { cxxbridge01$string$drop(this); } + +String::String(const std::string &s) { + auto ptr = s.data(); + auto len = s.length(); + if (!cxxbridge01$string$from(this, ptr, len)) { + throw std::invalid_argument("data for rust::String is not utf-8"); + } +} + +String::String(const char *s) { + auto len = strlen(s); + if (!cxxbridge01$string$from(this, s, len)) { + throw std::invalid_argument("data for rust::String is not utf-8"); + } +} + +String &String::operator=(const String &other) noexcept { + if (this != &other) { + cxxbridge01$string$drop(this); + cxxbridge01$string$clone(this, other); + } + return *this; +} + +String &String::operator=(String &&other) noexcept { + if (this != &other) { + cxxbridge01$string$drop(this); + this->repr = other.repr; + cxxbridge01$string$new(&other); + } + return *this; +} + +String::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *String::data() const noexcept { + return cxxbridge01$string$ptr(this); +} + +size_t String::size() const noexcept { return cxxbridge01$string$len(this); } + +size_t String::length() const noexcept { return cxxbridge01$string$len(this); } + +String::String(unsafe_bitcopy_t, const String &bits) noexcept + : repr(bits.repr) {} + +std::ostream &operator<<(std::ostream &os, const String &s) { + os.write(s.data(), s.size()); + return os; +} + +Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + +Str::Str(const Str &) noexcept = default; + +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + throw std::invalid_argument("data for rust::Str is not utf-8"); + } +} + +Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { + if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + throw std::invalid_argument("data for rust::Str is not utf-8"); + } +} + +Str &Str::operator=(Str other) noexcept { + this->repr = other.repr; + return *this; +} + +Str::operator std::string() const { + return std::string(this->data(), this->size()); +} + +const char *Str::data() const noexcept { return this->repr.ptr; } + +size_t Str::size() const noexcept { return this->repr.len; } + +size_t Str::length() const noexcept { return this->repr.len; } + +Str::Str(Repr repr_) noexcept : repr(repr_) {} + +Str::operator Repr() noexcept { return this->repr; } + +std::ostream &operator<<(std::ostream &os, const Str &s) { + os.write(s.data(), s.size()); + return os; +} + +} // namespace cxxbridge01 +} // namespace rust + +extern "C" { +void cxxbridge01$unique_ptr$std$string$null( + std::unique_ptr *ptr) noexcept { + new (ptr) std::unique_ptr(); +} +void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, + std::string *value) noexcept { + new (ptr) std::unique_ptr(new std::string(std::move(*value))); +} +void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, + std::string *raw) noexcept { + new (ptr) std::unique_ptr(raw); +} +const std::string *cxxbridge01$unique_ptr$std$string$get( + const std::unique_ptr &ptr) noexcept { + return ptr.get(); +} +std::string *cxxbridge01$unique_ptr$std$string$release( + std::unique_ptr &ptr) noexcept { + return ptr.release(); +} +void cxxbridge01$unique_ptr$std$string$drop( + std::unique_ptr *ptr) noexcept { + ptr->~unique_ptr(); +} +} // extern "C" diff --git a/src/cxxbridge.cc b/src/cxxbridge.cc deleted file mode 100644 index b44204d..0000000 --- a/src/cxxbridge.cc +++ /dev/null @@ -1,166 +0,0 @@ -#include "../include/cxxbridge.h" -#include -#include -#include -#include - -extern "C" { -const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { - return s.data(); -} - -size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { - return s.length(); -} - -// rust::String -void cxxbridge01$string$new(rust::String *self) noexcept; -void cxxbridge01$string$clone(rust::String *self, - const rust::String &other) noexcept; -bool cxxbridge01$string$from(rust::String *self, const char *ptr, - size_t len) noexcept; -void cxxbridge01$string$drop(rust::String *self) noexcept; -const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge01$string$len(const rust::String *self) noexcept; - -// rust::Str -bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; -} // extern "C" - -namespace rust { -inline namespace cxxbridge01 { - -String::String() noexcept { cxxbridge01$string$new(this); } - -String::String(const String &other) noexcept { - cxxbridge01$string$clone(this, other); -} - -String::String(String &&other) noexcept { - this->repr = other.repr; - cxxbridge01$string$new(&other); -} - -String::~String() noexcept { cxxbridge01$string$drop(this); } - -String::String(const std::string &s) { - auto ptr = s.data(); - auto len = s.length(); - if (!cxxbridge01$string$from(this, ptr, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); - } -} - -String::String(const char *s) { - auto len = strlen(s); - if (!cxxbridge01$string$from(this, s, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); - } -} - -String &String::operator=(const String &other) noexcept { - if (this != &other) { - cxxbridge01$string$drop(this); - cxxbridge01$string$clone(this, other); - } - return *this; -} - -String &String::operator=(String &&other) noexcept { - if (this != &other) { - cxxbridge01$string$drop(this); - this->repr = other.repr; - cxxbridge01$string$new(&other); - } - return *this; -} - -String::operator std::string() const { - return std::string(this->data(), this->size()); -} - -const char *String::data() const noexcept { - return cxxbridge01$string$ptr(this); -} - -size_t String::size() const noexcept { return cxxbridge01$string$len(this); } - -size_t String::length() const noexcept { return cxxbridge01$string$len(this); } - -String::String(unsafe_bitcopy_t, const String &bits) noexcept - : repr(bits.repr) {} - -std::ostream &operator<<(std::ostream &os, const String &s) { - os.write(s.data(), s.size()); - return os; -} - -Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} - -Str::Str(const Str &) noexcept = default; - -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); - } -} - -Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); - } -} - -Str &Str::operator=(Str other) noexcept { - this->repr = other.repr; - return *this; -} - -Str::operator std::string() const { - return std::string(this->data(), this->size()); -} - -const char *Str::data() const noexcept { return this->repr.ptr; } - -size_t Str::size() const noexcept { return this->repr.len; } - -size_t Str::length() const noexcept { return this->repr.len; } - -Str::Str(Repr repr_) noexcept : repr(repr_) {} - -Str::operator Repr() noexcept { return this->repr; } - -std::ostream &operator<<(std::ostream &os, const Str &s) { - os.write(s.data(), s.size()); - return os; -} - -} // namespace cxxbridge01 -} // namespace rust - -extern "C" { -void cxxbridge01$unique_ptr$std$string$null( - std::unique_ptr *ptr) noexcept { - new (ptr) std::unique_ptr(); -} -void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, - std::string *value) noexcept { - new (ptr) std::unique_ptr(new std::string(std::move(*value))); -} -void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, - std::string *raw) noexcept { - new (ptr) std::unique_ptr(raw); -} -const std::string *cxxbridge01$unique_ptr$std$string$get( - const std::unique_ptr &ptr) noexcept { - return ptr.get(); -} -std::string *cxxbridge01$unique_ptr$std$string$release( - std::unique_ptr &ptr) noexcept { - return ptr.release(); -} -void cxxbridge01$unique_ptr$std$string$drop( - std::unique_ptr *ptr) noexcept { - ptr->~unique_ptr(); -} -} // extern "C" diff --git a/src/lib.rs b/src/lib.rs index 006159a..316918f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,9 +310,9 @@ //! //! //! -//! The C++ API of the `cxxbridge` namespace is defined by the -//! *include/cxxbridge.h* file in https://github.com/dtolnay/cxx. You will need -//! to include this header in your C++ code when working with those types. +//! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file +//! in https://github.com/dtolnay/cxx. You will need to include this header in +//! your C++ code when working with those types. //! //! The following types are intended to be supported "soon" but are just not //! implemented yet. I don't expect any of these to be hard to make work but @@ -463,10 +463,10 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let mut build = paths::cc_build(); build.file(&bridge_path); - let ref cxxbridge_h = paths::include_dir()?.join("cxxbridge").join("cxxbridge.h"); - let _ = fs::create_dir_all(cxxbridge_h.parent().unwrap()); - let _ = fs::remove_file(cxxbridge_h); - let _ = fs::write(cxxbridge_h, gen::include::HEADER); + let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); + let _ = fs::create_dir_all(cxx_h.parent().unwrap()); + let _ = fs::remove_file(cxx_h); + let _ = fs::write(cxx_h, gen::include::HEADER); Ok(build) } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f41cc5e..7cff6fa 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -1,5 +1,5 @@ #pragma once -#include "cxxbridge/cxxbridge.h" +#include "rust/cxx.h" #include #include From bfc8dced0cc9758fbf918b8e6a69dad39e01b1ee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 12 2020 07:48:04 +0000 Subject: [PATCH 117/2232] Set html_root_url --- diff --git a/Cargo.toml b/Cargo.toml index 886e01b..47a45fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.1.2" +version = "0.1.2" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge01" diff --git a/src/lib.rs b/src/lib.rs index 316918f..ffa8352 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,6 +329,7 @@ //! tbdstd::unordered_map<K, V> //! +#![doc(html_root_url = "https://docs.rs/cxx/0.1.2")] #![deny(improper_ctypes)] #![allow( clippy::large_enum_variant, From d763f186265c680503b8630cfe19772af1397b87 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 12 2020 07:50:19 +0000 Subject: [PATCH 118/2232] Linkify some links to the github repo --- diff --git a/src/lib.rs b/src/lib.rs index ffa8352..47e5367 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ //! # Example //! //! A runnable version of this example is provided under the *demo-rs* directory -//! of https://github.com/dtolnay/cxx (with the C++ side of the implementation +//! of [https://github.com/dtolnay/cxx] (with the C++ side of the implementation //! in the *demo-cxx* directory). To try it out, jump into demo-rs and run //! `cargo run`. //! @@ -240,7 +240,7 @@ //! For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate //! way of invoking the C++ code generator as a standalone command line tool. //! The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be -//! built from the *cmd* directory of https://github.com/dtolnay/cxx. +//! built from the *cmd* directory of [https://github.com/dtolnay/cxx]. //! //! ```bash //! $ cargo install cxxbridge-cmd @@ -311,7 +311,7 @@ //! //! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file -//! in https://github.com/dtolnay/cxx. You will need to include this header in +//! in [https://github.com/dtolnay/cxx]. You will need to include this header in //! your C++ code when working with those types. //! //! The following types are intended to be supported "soon" but are just not @@ -328,6 +328,8 @@ //! tbdstd::map<K, V> //! tbdstd::unordered_map<K, V> //! +//! +//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![doc(html_root_url = "https://docs.rs/cxx/0.1.2")] #![deny(improper_ctypes)] @@ -406,8 +408,9 @@ use std::process; /// ``` /// /// A runnable working setup with this build script is shown in the -/// *demo-rs* and *demo-cxx* directories of -/// [https://github.com/dtolnay/cxx](https://github.com/dtolnay/cxx). +/// *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. +/// +/// [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx /// ///
/// From 75974b35c6c2cc42ac48728b173aad9ed87d5b47 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 12 2020 07:50:23 +0000 Subject: [PATCH 119/2232] Update lockfile for next release --- diff --git a/third-party/BUCK b/third-party/BUCK index 0a5063f..7dd18fe 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -67,7 +67,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.10/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.11/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -79,7 +79,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.10/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.11/src/**"]), proc_macro = True, deps = [ ":proc-macro2", @@ -107,7 +107,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.2/src/**"]), + srcs = glob(["vendor/quote-1.0.3/src/**"]), visibility = ["PUBLIC"], features = ["proc-macro"], deps = [":proc-macro2"], diff --git a/third-party/BUILD b/third-party/BUILD index 697c125..93e5426 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -72,7 +72,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.10/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.11/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -84,7 +84,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.10/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.11/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", @@ -112,7 +112,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.2/src/**"]), + srcs = glob(["vendor/quote-1.0.3/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], deps = [":proc-macro2"], diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 4e6cc7d..53cfa87 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a857f7c61b149c868eb7e40311b48502fcc924744fb73191962748643336568" +checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075d00534b62f176b55a48b68319be2f3fc05616d68ecd2bcb66bac0a49170e1" +checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" dependencies = [ "proc-macro2", "quote", @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ "proc-macro2", ] @@ -374,9 +374,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ff1b18659a2218332848d76ad1c867ce4c6ee37b085e6bc8de9a6d11401220" +checksum = "24b4e093c5ed1a60b22557090120aa14f90ca801549c0949d775ea07c1407720" dependencies = [ "glob", "lazy_static", From f51dc4d0bdf5a878a3d3d378a57af58e04ce59bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 12 2020 07:50:40 +0000 Subject: [PATCH 120/2232] Release 0.2.0 --- diff --git a/Cargo.toml b/Cargo.toml index 47a45fe..188eca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.1.2" # remember to update html_root_url +version = "0.2.0" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge01" @@ -19,7 +19,7 @@ anyhow = "1.0" cc = "1.0.49" codespan = "0.7" codespan-reporting = "0.7" -cxxbridge-macro = { version = "=0.1.2", path = "macro" } +cxxbridge-macro = { version = "=0.2.0", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/README.md b/README.md index 2886082..83be22e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ can be 100% safe. ```toml [dependencies] -cxx = "0.1" +cxx = "0.2" ``` *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March @@ -300,9 +300,9 @@ of functions. name in Rustname in C++restrictions Stringrust::String &strrust::Str -CxxStringstd::stringcannot be passed by value +CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type -UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index f39609a..4e83b81 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.1.2" +version = "0.2.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 8159ec7..707a19b 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.1.2" +version = "0.2.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -19,4 +19,4 @@ quote = "1.0" syn = { version = "1.0", features = ["full"] } [dev-dependencies] -cxx = { version = "0.1", path = ".." } +cxx = { version = "0.2", path = ".." } diff --git a/src/lib.rs b/src/lib.rs index 47e5367..c65baf1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -304,9 +304,9 @@ //! name in Rustname in C++restrictions //! Stringrust::String //! &strrust::Str -//! CxxStringstd::stringcannot be passed by value +//! CxxStringstd::stringcannot be passed by value //! Box<T>rust::Box<T>cannot hold opaque C++ type -//! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +//! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type //! //! //! diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 53cfa87..b0ac2a8 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -75,7 +75,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anyhow", "cc", @@ -101,7 +101,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anyhow", "codespan", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.1.2" +version = "0.2.0" dependencies = [ "cxx", "proc-macro2", From 3383ae719f11cf25f13247ddcada4517fd819191 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 08:14:11 +0000 Subject: [PATCH 121/2232] Add f32 and f64 types --- diff --git a/gen/write.rs b/gen/write.rs index 9ab98fa..6ff04e5 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -86,7 +86,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => out.include.cstdint = true, Some(CxxString) => out.include.string = true, - Some(Bool) | Some(RustString) | None => {} + Some(Bool) | Some(F32) | Some(F64) | Some(RustString) | None => {} }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, @@ -446,6 +446,8 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I32) => write!(out, "int32_t"), Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), Some(CxxString) => write!(out, "::std::string"), Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), diff --git a/syntax/atom.rs b/syntax/atom.rs index 9fb6554..0b3fd75 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -14,6 +14,8 @@ pub enum Atom { I32, I64, Isize, + F32, + F64, CxxString, RustString, } @@ -32,6 +34,8 @@ impl Atom { "i16" => Some(I16), "i32" => Some(I32), "i64" => Some(I64), + "f32" => Some(F32), + "f64" => Some(F64), "isize" => Some(Isize), "CxxString" => Some(CxxString), "String" => Some(RustString), From 2b719cb78ef80bf6203fbe9023e297554468c316 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 08:26:27 +0000 Subject: [PATCH 122/2232] Merge pull request #65 from dtolnay/float Add f32 and f64 types --- diff --git a/gen/write.rs b/gen/write.rs index 9ab98fa..6ff04e5 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -86,7 +86,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => out.include.cstdint = true, Some(CxxString) => out.include.string = true, - Some(Bool) | Some(RustString) | None => {} + Some(Bool) | Some(F32) | Some(F64) | Some(RustString) | None => {} }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, @@ -446,6 +446,8 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I32) => write!(out, "int32_t"), Some(I64) => write!(out, "int64_t"), Some(Isize) => write!(out, "ssize_t"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), Some(CxxString) => write!(out, "::std::string"), Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), diff --git a/syntax/atom.rs b/syntax/atom.rs index 9fb6554..0b3fd75 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -14,6 +14,8 @@ pub enum Atom { I32, I64, Isize, + F32, + F64, CxxString, RustString, } @@ -32,6 +34,8 @@ impl Atom { "i16" => Some(I16), "i32" => Some(I32), "i64" => Some(I64), + "f32" => Some(F32), + "f64" => Some(F64), "isize" => Some(Isize), "CxxString" => Some(CxxString), "String" => Some(RustString), From 8c7304998e302c8cb93487d85de326c0ef62ec9a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 08:29:23 +0000 Subject: [PATCH 123/2232] Bump inline namespace to match minor version --- diff --git a/Cargo.toml b/Cargo.toml index 188eca3..f81cdb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "cxx" version = "0.2.0" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" -links = "cxxbridge01" +links = "cxxbridge02" license = "MIT OR Apache-2.0" description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" diff --git a/build.rs b/build.rs index bdda779..5173658 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,7 @@ fn main() { cc::Build::new() .file("src/cxx.cc") .flag("-std=c++11") - .compile("cxxbridge01"); + .compile("cxxbridge02"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); } diff --git a/gen/write.rs b/gen/write.rs index 6ff04e5..f74fb74 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -123,7 +123,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge01"); + out.begin_block("inline namespace cxxbridge02"); if needs_rust_box || needs_manually_drop || needs_maybe_uninit { writeln!(out, "// #include \"rust/cxx.h\""); @@ -131,7 +131,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_rust_box { out.next_section(); - for line in include::get("CXXBRIDGE01_RUST_BOX").lines() { + for line in include::get("CXXBRIDGE02_RUST_BOX").lines() { if !line.trim_start().starts_with("//") { writeln!(out, "{}", line); } @@ -161,7 +161,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } - out.end_block("namespace cxxbridge01"); + out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -195,7 +195,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge01${}(", efn.ident); + write!(out, "cxxbridge02${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -278,7 +278,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge01${}(", efn.ident); + write!(out, "cxxbridge02${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -352,7 +352,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for name in out.namespace.clone() { write!(out, "{}$", name); } - write!(out, "cxxbridge01${}(", efn.ident); + write!(out, "cxxbridge02${}(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -507,7 +507,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("extern \"C\""); out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge01"); + out.begin_block("inline namespace cxxbridge02"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -515,7 +515,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block("namespace cxxbridge01"); + out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -528,19 +528,19 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE01_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge01$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge01$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); - writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance); + writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); } fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { @@ -554,12 +554,12 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " return cxxbridge01$box${}$uninit(this);", instance); + writeln!(out, " return cxxbridge02$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " return cxxbridge01$box${}$drop(this);", instance); + writeln!(out, " return cxxbridge02$box${}$drop(this);", instance); writeln!(out, "}}"); } @@ -572,8 +572,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE01_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE01_UNIQUE_PTR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!( out, "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", @@ -586,14 +586,14 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { ); writeln!( out, - "void cxxbridge01$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( @@ -604,31 +604,31 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge01$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge01$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge01$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE01_UNIQUE_PTR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); } diff --git a/include/cxx.h b/include/cxx.h index 81bdad5..e02145b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -6,7 +6,7 @@ #include namespace rust { -inline namespace cxxbridge01 { +inline namespace cxxbridge02 { struct unsafe_bitcopy_t; @@ -71,8 +71,8 @@ private: Repr repr; }; -#ifndef CXXBRIDGE01_RUST_BOX -#define CXXBRIDGE01_RUST_BOX +#ifndef CXXBRIDGE02_RUST_BOX +#define CXXBRIDGE02_RUST_BOX template class Box final { public: using value_type = T; @@ -136,7 +136,7 @@ private: void drop() noexcept; T *ptr; }; -#endif // CXXBRIDGE01_RUST_BOX +#endif // CXXBRIDGE02_RUST_BOX std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -151,5 +151,5 @@ struct unsafe_bitcopy_t { }; constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -} // namespace cxxbridge01 +} // namespace cxxbridge02 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8519c1e..fcfa1d9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -140,7 +140,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = format!("{}cxxbridge01${}", namespace, ident); + let link_name = format!("{}cxxbridge02${}", namespace, ident); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] @@ -295,7 +295,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type expr = quote!(::std::ptr::write(__return, #expr)); } let ret = expand_extern_return_type(&efn.ret, types); - let link_name = format!("{}cxxbridge01${}", namespace, ident); + let link_name = format!("{}cxxbridge02${}", namespace, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); quote! { @@ -309,7 +309,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge01$box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -338,7 +338,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { - let prefix = format!("cxxbridge01$unique_ptr${}{}$", namespace, ident); + let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); diff --git a/src/cxx.cc b/src/cxx.cc index f7b02ec..f55d062 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -5,72 +5,72 @@ #include extern "C" { -const char *cxxbridge01$cxx_string$data(const std::string &s) noexcept { +const char *cxxbridge02$cxx_string$data(const std::string &s) noexcept { return s.data(); } -size_t cxxbridge01$cxx_string$length(const std::string &s) noexcept { +size_t cxxbridge02$cxx_string$length(const std::string &s) noexcept { return s.length(); } // rust::String -void cxxbridge01$string$new(rust::String *self) noexcept; -void cxxbridge01$string$clone(rust::String *self, +void cxxbridge02$string$new(rust::String *self) noexcept; +void cxxbridge02$string$clone(rust::String *self, const rust::String &other) noexcept; -bool cxxbridge01$string$from(rust::String *self, const char *ptr, +bool cxxbridge02$string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge01$string$drop(rust::String *self) noexcept; -const char *cxxbridge01$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge01$string$len(const rust::String *self) noexcept; +void cxxbridge02$string$drop(rust::String *self) noexcept; +const char *cxxbridge02$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge02$string$len(const rust::String *self) noexcept; // rust::Str -bool cxxbridge01$str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge02$str$valid(const char *ptr, size_t len) noexcept; } // extern "C" namespace rust { -inline namespace cxxbridge01 { +inline namespace cxxbridge02 { -String::String() noexcept { cxxbridge01$string$new(this); } +String::String() noexcept { cxxbridge02$string$new(this); } String::String(const String &other) noexcept { - cxxbridge01$string$clone(this, other); + cxxbridge02$string$clone(this, other); } String::String(String &&other) noexcept { this->repr = other.repr; - cxxbridge01$string$new(&other); + cxxbridge02$string$new(&other); } -String::~String() noexcept { cxxbridge01$string$drop(this); } +String::~String() noexcept { cxxbridge02$string$drop(this); } String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); - if (!cxxbridge01$string$from(this, ptr, len)) { + if (!cxxbridge02$string$from(this, ptr, len)) { throw std::invalid_argument("data for rust::String is not utf-8"); } } String::String(const char *s) { auto len = strlen(s); - if (!cxxbridge01$string$from(this, s, len)) { + if (!cxxbridge02$string$from(this, s, len)) { throw std::invalid_argument("data for rust::String is not utf-8"); } } String &String::operator=(const String &other) noexcept { if (this != &other) { - cxxbridge01$string$drop(this); - cxxbridge01$string$clone(this, other); + cxxbridge02$string$drop(this); + cxxbridge02$string$clone(this, other); } return *this; } String &String::operator=(String &&other) noexcept { if (this != &other) { - cxxbridge01$string$drop(this); + cxxbridge02$string$drop(this); this->repr = other.repr; - cxxbridge01$string$new(&other); + cxxbridge02$string$new(&other); } return *this; } @@ -80,12 +80,12 @@ String::operator std::string() const { } const char *String::data() const noexcept { - return cxxbridge01$string$ptr(this); + return cxxbridge02$string$ptr(this); } -size_t String::size() const noexcept { return cxxbridge01$string$len(this); } +size_t String::size() const noexcept { return cxxbridge02$string$len(this); } -size_t String::length() const noexcept { return cxxbridge01$string$len(this); } +size_t String::length() const noexcept { return cxxbridge02$string$len(this); } String::String(unsafe_bitcopy_t, const String &bits) noexcept : repr(bits.repr) {} @@ -100,13 +100,13 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const Str &) noexcept = default; Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { - if (!cxxbridge01$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } } @@ -135,31 +135,31 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } -} // namespace cxxbridge01 +} // namespace cxxbridge02 } // namespace rust extern "C" { -void cxxbridge01$unique_ptr$std$string$null( +void cxxbridge02$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge01$unique_ptr$std$string$new(std::unique_ptr *ptr, +void cxxbridge02$unique_ptr$std$string$new(std::unique_ptr *ptr, std::string *value) noexcept { new (ptr) std::unique_ptr(new std::string(std::move(*value))); } -void cxxbridge01$unique_ptr$std$string$raw(std::unique_ptr *ptr, +void cxxbridge02$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); } -const std::string *cxxbridge01$unique_ptr$std$string$get( +const std::string *cxxbridge02$unique_ptr$std$string$get( const std::unique_ptr &ptr) noexcept { return ptr.get(); } -std::string *cxxbridge01$unique_ptr$std$string$release( +std::string *cxxbridge02$unique_ptr$std$string$release( std::unique_ptr &ptr) noexcept { return ptr.release(); } -void cxxbridge01$unique_ptr$std$string$drop( +void cxxbridge02$unique_ptr$std$string$drop( std::unique_ptr *ptr) noexcept { ptr->~unique_ptr(); } diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 40a1731..f2cca5b 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -4,9 +4,9 @@ use std::slice; use std::str::{self, Utf8Error}; extern "C" { - #[link_name = "cxxbridge01$cxx_string$data"] + #[link_name = "cxxbridge02$cxx_string$data"] fn string_data(_: &CxxString) -> *const u8; - #[link_name = "cxxbridge01$cxx_string$length"] + #[link_name = "cxxbridge02$cxx_string$length"] fn string_length(_: &CxxString) -> usize; } diff --git a/src/rust_str.rs b/src/rust_str.rs index 3d8a9f0..5b8eaed 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -23,7 +23,7 @@ impl RustStr { } } -#[export_name = "cxxbridge01$str$valid"] +#[export_name = "cxxbridge02$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/rust_string.rs b/src/rust_string.rs index 345d969..258576d 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -30,17 +30,17 @@ impl RustString { } } -#[export_name = "cxxbridge01$string$new"] +#[export_name = "cxxbridge02$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge01$string$clone"] +#[export_name = "cxxbridge02$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge01$string$from"] +#[export_name = "cxxbridge02$string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -56,17 +56,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge01$string$drop"] +#[export_name = "cxxbridge02$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge01$string$ptr"] +#[export_name = "cxxbridge02$string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge01$string$len"] +#[export_name = "cxxbridge02$string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 19718bb..94b6e25 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -130,17 +130,17 @@ pub unsafe trait UniquePtrTarget { } extern "C" { - #[link_name = "cxxbridge01$unique_ptr$std$string$null"] + #[link_name = "cxxbridge02$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge01$unique_ptr$std$string$new"] + #[link_name = "cxxbridge02$unique_ptr$std$string$new"] fn unique_ptr_std_string_new(this: *mut *mut c_void, value: *mut CxxString); - #[link_name = "cxxbridge01$unique_ptr$std$string$raw"] + #[link_name = "cxxbridge02$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); - #[link_name = "cxxbridge01$unique_ptr$std$string$get"] + #[link_name = "cxxbridge02$unique_ptr$std$string$get"] fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; - #[link_name = "cxxbridge01$unique_ptr$std$string$release"] + #[link_name = "cxxbridge02$unique_ptr$std$string$release"] fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; - #[link_name = "cxxbridge01$unique_ptr$std$string$drop"] + #[link_name = "cxxbridge02$unique_ptr$std$string$drop"] fn unique_ptr_std_string_drop(this: *mut *mut c_void); } From 218e237cac9cfda6982c52eda11d1d6d9be5b83e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 08:34:05 +0000 Subject: [PATCH 124/2232] Remove blank builtin types table row --- diff --git a/README.md b/README.md index 83be22e..41639c2 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,6 @@ of functions. CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type - The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in diff --git a/src/lib.rs b/src/lib.rs index c65baf1..21d6a8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -307,7 +307,6 @@ //! CxxStringstd::stringcannot be passed by value //! Box<T>rust::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type -//! //! //! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file From 239d05fac630bd8a8ea45ae0b39e7f47c7bb4573 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 08:36:52 +0000 Subject: [PATCH 125/2232] Add Arc and shared_ptr to tbd table Closes #18. --- diff --git a/README.md b/README.md index 41639c2..801c14d 100644 --- a/README.md +++ b/README.md @@ -319,9 +319,11 @@ matter of designing a nice API for each in its non-native language. Vec<T>tbd BTreeMap<K, V>tbd HashMap<K, V>tbd +Arc<T>tbd tbdstd::vector<T> tbdstd::map<K, V> tbdstd::unordered_map<K, V> +tbdstd::shared_ptr<T>
diff --git a/src/lib.rs b/src/lib.rs index 21d6a8b..14cc0cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -323,9 +323,11 @@ //! Vec<T>tbd //! BTreeMap<K, V>tbd //! HashMap<K, V>tbd +//! Arc<T>tbd //! tbdstd::vector<T> //! tbdstd::map<K, V> //! tbdstd::unordered_map<K, V> +//! tbdstd::shared_ptr<T> //! //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx From 9542f227db088c5b0e98fdaa14f1bfef7f2441fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 13 2020 20:55:28 +0000 Subject: [PATCH 126/2232] Clean up Atom::from match arm order --- diff --git a/syntax/atom.rs b/syntax/atom.rs index 0b3fd75..eeea831 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -34,9 +34,9 @@ impl Atom { "i16" => Some(I16), "i32" => Some(I32), "i64" => Some(I64), + "isize" => Some(Isize), "f32" => Some(F32), "f64" => Some(F64), - "isize" => Some(Isize), "CxxString" => Some(CxxString), "String" => Some(RustString), _ => None, From 2fb14e934be453c0d4ad4840ab0d5b8c85d00bed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:11:40 +0000 Subject: [PATCH 127/2232] Add Type::Void variant Not currently usable as a function argument or explicit return value, but will be required when we introduce Result for the case of fallible void functions, whose return type will be Result<()>. --- diff --git a/gen/write.rs b/gen/write.rs index f74fb74..3cd3683 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -472,6 +472,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } + Type::Void(_) => unreachable!(), } } @@ -480,6 +481,7 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), Type::Ref(_) => {} + Type::Void(_) => unreachable!(), } } diff --git a/syntax/check.rs b/syntax/check.rs index df633de..81e1f05 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -161,6 +161,7 @@ fn describe(ty: &Type, types: &Types) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Void(_) => "()".to_owned(), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index c5cc1de..1acb9a5 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -9,9 +9,11 @@ pub mod ident; mod impls; mod parse; pub mod set; +mod span; mod tokens; pub mod types; +use self::span::Span; use proc_macro2::Ident; use syn::{LitStr, Token}; @@ -70,6 +72,7 @@ pub enum Type { UniquePtr(Box), Ref(Box), Str(Box), + Void(Span), } pub struct Ty1 { diff --git a/syntax/span.rs b/syntax/span.rs new file mode 100644 index 0000000..c78567e --- /dev/null +++ b/syntax/span.rs @@ -0,0 +1,16 @@ +use std::hash::{Hash, Hasher}; + +#[derive(Copy, Clone)] +pub struct Span(pub proc_macro2::Span); + +impl Hash for Span { + fn hash(&self, _state: &mut H) {} +} + +impl Eq for Span {} + +impl PartialEq for Span { + fn eq(&self, _other: &Span) -> bool { + true + } +} diff --git a/syntax/tokens.rs b/syntax/tokens.rs index e97509b..4ddc1a6 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -16,6 +16,7 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Void(span) => tokens.extend(quote_spanned!(span.0=> ())), } } } diff --git a/syntax/types.rs b/syntax/types.rs index 50f13b1..f9f87f3 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -23,7 +23,7 @@ impl<'a> Types<'a> { fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); match ty { - Type::Ident(_) | Type::Str(_) => {} + Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), } From fb134ed1be77395bd5ae5bd369a44d0b459e7925 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:17:48 +0000 Subject: [PATCH 128/2232] Implement parsing () type --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 1acb9a5..e702be0 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,13 +13,13 @@ mod span; mod tokens; pub mod types; -use self::span::Span; use proc_macro2::Ident; use syn::{LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; pub use self::parse::parse_items; +pub use self::span::Span; pub use self::types::Types; pub enum Api { diff --git a/syntax/parse.rs b/syntax/parse.rs index efe6ff8..1453b30 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,5 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Receiver, Ref, Struct, Ty1, Type, Var, + self, attrs, error, Api, Atom, Doc, ExternFn, ExternType, Receiver, Ref, Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::quote; @@ -251,6 +251,9 @@ fn parse_type(ty: &RustType) -> Result { } } } + RustType::Tuple(ty) if ty.elems.is_empty() => { + return Ok(Type::Void(syntax::Span(ty.paren_token.span))); + } _ => {} } Err(Error::new_spanned(ty, "unsupported type")) From c21b20ac0fc30845ce1b25377b7c09d9d6aef300 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:25:34 +0000 Subject: [PATCH 129/2232] Handwrite boilerplate impls for Type This allows dropping the Span wrapper that was only needed for the Eq and Hash support. --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 8153f03..741431a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,5 +1,36 @@ -use crate::syntax::{Ref, Ty1}; +use crate::syntax::{Ref, Ty1, Type}; use std::hash::{Hash, Hasher}; +use std::mem; + +impl Hash for Type { + fn hash(&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::Ref(t) => t.hash(state), + Type::Str(t) => t.hash(state), + Type::Void(_) => {} + } + } +} + +impl Eq for Type {} + +impl PartialEq for Type { + fn eq(&self, other: &Type) -> 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::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, + (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, + (Type::Void(_), Type::Void(_)) => true, + (_, _) => false, + } + } +} impl Eq for Ty1 {} diff --git a/syntax/mod.rs b/syntax/mod.rs index e702be0..39198cc 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -65,7 +65,6 @@ pub struct Receiver { pub ident: Ident, } -#[derive(Hash, Eq, PartialEq)] pub enum Type { Ident(Ident), RustBox(Box), From d0bb3646cf05ce8b96bc3fb7378d71a2001c68d8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:27:49 +0000 Subject: [PATCH 130/2232] Remove Span wrapper type --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 39198cc..8c7e5ca 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -9,17 +9,15 @@ pub mod ident; mod impls; mod parse; pub mod set; -mod span; mod tokens; pub mod types; -use proc_macro2::Ident; +use proc_macro2::{Ident, Span}; use syn::{LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; pub use self::parse::parse_items; -pub use self::span::Span; pub use self::types::Types; pub enum Api { diff --git a/syntax/parse.rs b/syntax/parse.rs index 1453b30..e059ae5 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,5 @@ use crate::syntax::{ - self, attrs, error, Api, Atom, Doc, ExternFn, ExternType, Receiver, Ref, Struct, Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Receiver, Ref, Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::quote; @@ -252,7 +252,7 @@ fn parse_type(ty: &RustType) -> Result { } } RustType::Tuple(ty) if ty.elems.is_empty() => { - return Ok(Type::Void(syntax::Span(ty.paren_token.span))); + return Ok(Type::Void(ty.paren_token.span)); } _ => {} } diff --git a/syntax/span.rs b/syntax/span.rs deleted file mode 100644 index c78567e..0000000 --- a/syntax/span.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::hash::{Hash, Hasher}; - -#[derive(Copy, Clone)] -pub struct Span(pub proc_macro2::Span); - -impl Hash for Span { - fn hash(&self, _state: &mut H) {} -} - -impl Eq for Span {} - -impl PartialEq for Span { - fn eq(&self, _other: &Span) -> bool { - true - } -} diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 4ddc1a6..52c47d5 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -16,7 +16,7 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), - Type::Void(span) => tokens.extend(quote_spanned!(span.0=> ())), + Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } } } From 1fa1ae4cec168af342b63b7c2e30a25d7ce56075 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:39:58 +0000 Subject: [PATCH 131/2232] Implement restrictions on placement of () --- diff --git a/syntax/check.rs b/syntax/check.rs index 81e1f05..ef3faff 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; use proc_macro2::Ident; use syn::{Error, Result}; @@ -40,6 +40,11 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { } errors.push(unsupported_unique_ptr_target(ptr)); } + Type::Ref(ty) => { + if let Type::Void(_) = ty.inner { + errors.push(unsupported_reference_type(ty)); + } + } _ => {} } } @@ -94,6 +99,7 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { fn is_unsized(ty: &Type, types: &Types) -> bool { let ident = match ty { Type::Ident(ident) => ident, + Type::Void(_) => return true, _ => return false, }; ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) @@ -169,6 +175,10 @@ fn unsupported_type(ident: &Ident) -> Error { Error::new(ident.span(), "unsupported type") } +fn unsupported_reference_type(ty: &Ref) -> Error { + Error::new_spanned(ty, "unsupported reference type") +} + fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error { Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg) } From 64181b127ed91035a9608bdefb8cb0a151479c83 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:42:54 +0000 Subject: [PATCH 132/2232] Ignore Void in return position --- diff --git a/syntax/parse.rs b/syntax/parse.rs index e059ae5..67bb52b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -180,7 +180,10 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { } let ret = match &foreign_fn.sig.output { ReturnType::Default => None, - ReturnType::Type(_, ty) => Some(parse_type(ty)?), + ReturnType::Type(_, ty) => match parse_type(ty)? { + Type::Void(_) => None, + ty => Some(ty), + }, }; let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; From 993918b0cf69d755aef6504465e75c0eb768df98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 06:54:39 +0000 Subject: [PATCH 133/2232] Merge pull request #69 from dtolnay/void Add Type::Void variant --- diff --git a/gen/write.rs b/gen/write.rs index f74fb74..3cd3683 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -472,6 +472,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } + Type::Void(_) => unreachable!(), } } @@ -480,6 +481,7 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), Type::Ref(_) => {} + Type::Void(_) => unreachable!(), } } diff --git a/syntax/check.rs b/syntax/check.rs index df633de..ef3faff 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; use proc_macro2::Ident; use syn::{Error, Result}; @@ -40,6 +40,11 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { } errors.push(unsupported_unique_ptr_target(ptr)); } + Type::Ref(ty) => { + if let Type::Void(_) = ty.inner { + errors.push(unsupported_reference_type(ty)); + } + } _ => {} } } @@ -94,6 +99,7 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { fn is_unsized(ty: &Type, types: &Types) -> bool { let ident = match ty { Type::Ident(ident) => ident, + Type::Void(_) => return true, _ => return false, }; ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) @@ -161,6 +167,7 @@ fn describe(ty: &Type, types: &Types) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Void(_) => "()".to_owned(), } } @@ -168,6 +175,10 @@ fn unsupported_type(ident: &Ident) -> Error { Error::new(ident.span(), "unsupported type") } +fn unsupported_reference_type(ty: &Ref) -> Error { + Error::new_spanned(ty, "unsupported reference type") +} + fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error { Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg) } diff --git a/syntax/impls.rs b/syntax/impls.rs index 8153f03..741431a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,5 +1,36 @@ -use crate::syntax::{Ref, Ty1}; +use crate::syntax::{Ref, Ty1, Type}; use std::hash::{Hash, Hasher}; +use std::mem; + +impl Hash for Type { + fn hash(&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::Ref(t) => t.hash(state), + Type::Str(t) => t.hash(state), + Type::Void(_) => {} + } + } +} + +impl Eq for Type {} + +impl PartialEq for Type { + fn eq(&self, other: &Type) -> 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::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, + (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, + (Type::Void(_), Type::Void(_)) => true, + (_, _) => false, + } + } +} impl Eq for Ty1 {} diff --git a/syntax/mod.rs b/syntax/mod.rs index c5cc1de..8c7e5ca 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -12,7 +12,7 @@ pub mod set; mod tokens; pub mod types; -use proc_macro2::Ident; +use proc_macro2::{Ident, Span}; use syn::{LitStr, Token}; pub use self::atom::Atom; @@ -63,13 +63,13 @@ pub struct Receiver { pub ident: Ident, } -#[derive(Hash, Eq, PartialEq)] pub enum Type { Ident(Ident), RustBox(Box), UniquePtr(Box), Ref(Box), Str(Box), + Void(Span), } pub struct Ty1 { diff --git a/syntax/parse.rs b/syntax/parse.rs index efe6ff8..67bb52b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -180,7 +180,10 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { } let ret = match &foreign_fn.sig.output { ReturnType::Default => None, - ReturnType::Type(_, ty) => Some(parse_type(ty)?), + ReturnType::Type(_, ty) => match parse_type(ty)? { + Type::Void(_) => None, + ty => Some(ty), + }, }; let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; @@ -251,6 +254,9 @@ fn parse_type(ty: &RustType) -> Result { } } } + RustType::Tuple(ty) if ty.elems.is_empty() => { + return Ok(Type::Void(ty.paren_token.span)); + } _ => {} } Err(Error::new_spanned(ty, "unsupported type")) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index e97509b..52c47d5 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -16,6 +16,7 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } } } diff --git a/syntax/types.rs b/syntax/types.rs index 50f13b1..f9f87f3 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -23,7 +23,7 @@ impl<'a> Types<'a> { fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); match ty { - Type::Ident(_) | Type::Str(_) => {} + Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), } From 30d214cca2f6cdd0f53d7548fbbd4439c314d7df Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 07:35:30 +0000 Subject: [PATCH 134/2232] Suppress some more clippy lints --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 06b3bbe..b2cde40 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -1,3 +1,10 @@ +#![allow( + clippy::inherent_to_string, + clippy::large_enum_variant, + clippy::new_without_default, + clippy::toplevel_ref_arg +)] + mod gen; mod syntax; diff --git a/macro/src/lib.rs b/macro/src/lib.rs index a211761..59433c1 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -1,4 +1,5 @@ #![allow( + clippy::inherent_to_string, clippy::large_enum_variant, clippy::new_without_default, clippy::or_fun_call, diff --git a/src/lib.rs b/src/lib.rs index 14cc0cb..24c5eaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -335,9 +335,11 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.1.2")] #![deny(improper_ctypes)] #![allow( + clippy::inherent_to_string, clippy::large_enum_variant, clippy::missing_safety_doc, clippy::module_inception, + clippy::needless_doctest_main, clippy::new_without_default, clippy::or_fun_call, clippy::ptr_arg, From 59b7edea2737cdbbf86ff06f23d416cd58111e77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 07:36:15 +0000 Subject: [PATCH 135/2232] Parse Result return type --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 8c7e5ca..5b51648 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -50,6 +50,7 @@ pub struct ExternFn { pub receiver: Option, pub args: Vec, pub ret: Option, + pub throws: bool, pub semi_token: Token![;], } diff --git a/syntax/parse.rs b/syntax/parse.rs index 67bb52b..7bae707 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -178,13 +178,34 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { } } } + + let mut throws = false; let ret = match &foreign_fn.sig.output { ReturnType::Default => None, - ReturnType::Type(_, ty) => match parse_type(ty)? { - Type::Void(_) => None, - ty => Some(ty), - }, + ReturnType::Type(_, ret) => { + let mut ret = ret.as_ref(); + if let RustType::Path(ty) = ret { + let path = &ty.path; + if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { + let segment = &path.segments[0]; + let ident = segment.ident.clone(); + if let PathArguments::AngleBracketed(generic) = &segment.arguments { + if ident == "Result" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + ret = arg; + throws = true; + } + } + } + } + } + match parse_type(ret)? { + Type::Void(_) => None, + ty => Some(ty), + } + } }; + let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); @@ -196,12 +217,13 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { receiver, args, ret, + throws, semi_token, }) } fn parse_type(ty: &RustType) -> Result { - match &ty { + match ty { RustType::Reference(ty) => { let inner = parse_type(&ty.elem)?; let which = match &inner { From c0a166d790d3494ce8e397c2f169463a66e66b26 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 07:40:15 +0000 Subject: [PATCH 136/2232] Add error message to say Result is not implemented yet --- diff --git a/syntax/check.rs b/syntax/check.rs index ef3faff..80937bb 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -69,6 +69,12 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(return_by_value(ty, types)); } } + if efn.throws { + errors.push(Error::new_spanned( + efn, + "fallible functions are not implemented yet", + )); + } } _ => {} } From 3fe5e2c2204226ca2a60f7f93e57c7bb99ffa623 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 07:49:55 +0000 Subject: [PATCH 137/2232] Merge pull request #70 from dtolnay/result Parse Result return type --- diff --git a/syntax/check.rs b/syntax/check.rs index ef3faff..80937bb 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -69,6 +69,12 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(return_by_value(ty, types)); } } + if efn.throws { + errors.push(Error::new_spanned( + efn, + "fallible functions are not implemented yet", + )); + } } _ => {} } diff --git a/syntax/mod.rs b/syntax/mod.rs index 8c7e5ca..5b51648 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -50,6 +50,7 @@ pub struct ExternFn { pub receiver: Option, pub args: Vec, pub ret: Option, + pub throws: bool, pub semi_token: Token![;], } diff --git a/syntax/parse.rs b/syntax/parse.rs index 67bb52b..7bae707 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -178,13 +178,34 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { } } } + + let mut throws = false; let ret = match &foreign_fn.sig.output { ReturnType::Default => None, - ReturnType::Type(_, ty) => match parse_type(ty)? { - Type::Void(_) => None, - ty => Some(ty), - }, + ReturnType::Type(_, ret) => { + let mut ret = ret.as_ref(); + if let RustType::Path(ty) = ret { + let path = &ty.path; + if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { + let segment = &path.segments[0]; + let ident = segment.ident.clone(); + if let PathArguments::AngleBracketed(generic) = &segment.arguments { + if ident == "Result" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + ret = arg; + throws = true; + } + } + } + } + } + match parse_type(ret)? { + Type::Void(_) => None, + ty => Some(ty), + } + } }; + let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); @@ -196,12 +217,13 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { receiver, args, ret, + throws, semi_token, }) } fn parse_type(ty: &RustType) -> Result { - match &ty { + match ty { RustType::Reference(ty) => { let inner = parse_type(&ty.elem)?; let which = match &inner { From b606ce367ee173045b0df5be311efd94f862853c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 08:17:58 +0000 Subject: [PATCH 138/2232] Add CI on stable 1.42 --- diff --git a/.travis.yml b/.travis.yml index aa158b7..1cc7806 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: rust rust: - nightly - beta + - stable script: - cargo run --manifest-path demo-rs/Cargo.toml diff --git a/README.md b/README.md index 801c14d..ea82afb 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,7 @@ can be 100% safe. cxx = "0.2" ``` -*Compiler support: requires rustc 1.42+ (beta on January 30, stable on March -12)* +*Compiler support: requires rustc 1.42+*
diff --git a/WORKSPACE b/WORKSPACE index 3e3baed..4357b70 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,17 +24,15 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_42_beta_linux", + name = "rust_1_42_linux", exec_triple = "x86_64-unknown-linux-gnu", extra_target_triples = [], - iso_date = "2020-02-08", - version = "beta", + version = "1.42.0", ) rust_repository_set( - name = "rust_1_42_beta_darwin", + name = "rust_1_42_darwin", exec_triple = "x86_64-apple-darwin", extra_target_triples = [], - iso_date = "2020-02-08", - version = "beta", + version = "1.42.0", ) diff --git a/src/lib.rs b/src/lib.rs index 24c5eaf..48c3b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,7 @@ //! //!
//! -//! *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March -//! 12)* +//! *Compiler support: requires rustc 1.42+* //! //!
//! From 2932c9f133273d0a0787d78aac7330b1b6c4a121 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 16 2020 08:31:20 +0000 Subject: [PATCH 139/2232] Merge pull request #71 from dtolnay/stable Add CI on stable 1.42 --- diff --git a/.travis.yml b/.travis.yml index aa158b7..1cc7806 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: rust rust: - nightly - beta + - stable script: - cargo run --manifest-path demo-rs/Cargo.toml diff --git a/README.md b/README.md index 801c14d..ea82afb 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,7 @@ can be 100% safe. cxx = "0.2" ``` -*Compiler support: requires rustc 1.42+ (beta on January 30, stable on March -12)* +*Compiler support: requires rustc 1.42+*
diff --git a/WORKSPACE b/WORKSPACE index 3e3baed..4357b70 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,17 +24,15 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_42_beta_linux", + name = "rust_1_42_linux", exec_triple = "x86_64-unknown-linux-gnu", extra_target_triples = [], - iso_date = "2020-02-08", - version = "beta", + version = "1.42.0", ) rust_repository_set( - name = "rust_1_42_beta_darwin", + name = "rust_1_42_darwin", exec_triple = "x86_64-apple-darwin", extra_target_triples = [], - iso_date = "2020-02-08", - version = "beta", + version = "1.42.0", ) diff --git a/src/lib.rs b/src/lib.rs index 24c5eaf..48c3b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,7 @@ //! //!
//! -//! *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March -//! 12)* +//! *Compiler support: requires rustc 1.42+* //! //!
//! From da5bd272b6512cf70968497e7e4a6f1e2876bbc9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 04:53:22 +0000 Subject: [PATCH 140/2232] Add const assert macro for some static checks --- diff --git a/src/assert.rs b/src/assert.rs new file mode 100644 index 0000000..6159ce6 --- /dev/null +++ b/src/assert.rs @@ -0,0 +1,30 @@ +pub struct True; +pub struct False; + +pub trait ToBool { + type Bool: Sized; + const BOOL: Self::Bool; +} + +impl ToBool for [(); 0] { + type Bool = False; + const BOOL: Self::Bool = False; +} + +impl ToBool for [(); 1] { + type Bool = True; + const BOOL: Self::Bool = True; +} + +macro_rules! bool { + ($e:expr) => {{ + const EXPR: bool = $e; + <[(); EXPR as usize] as $crate::assert::ToBool>::BOOL + }}; +} + +macro_rules! const_assert { + ($e:expr) => { + const _: $crate::assert::True = bool!($e); + }; +} diff --git a/src/lib.rs b/src/lib.rs index 48c3b72..3744bda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,6 +349,9 @@ extern crate link_cplusplus; +#[macro_use] +mod assert; + mod cxx_string; mod error; mod gen; From 792d02222acf3fd0374aa59df0d285b7e4ec56cd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 04:57:49 +0000 Subject: [PATCH 141/2232] Add niche to RustStr This will be useful for enabling fallible functions to return Option as the error message if one occurred. --- diff --git a/src/rust_str.rs b/src/rust_str.rs index 5b8eaed..110889b 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -1,3 +1,5 @@ +use std::mem; +use std::ptr::NonNull; use std::slice; use std::str; @@ -5,20 +7,20 @@ use std::str; #[repr(C)] #[derive(Copy, Clone)] pub struct RustStr { - ptr: *const u8, + ptr: NonNull, len: usize, } impl RustStr { pub fn from(s: &str) -> Self { RustStr { - ptr: s.as_ptr(), + ptr: NonNull::from(s).cast::(), len: s.len(), } } pub unsafe fn as_str<'a>(self) -> &'a str { - let slice = slice::from_raw_parts(self.ptr, self.len); + let slice = slice::from_raw_parts(self.ptr.as_ptr(), self.len); str::from_utf8_unchecked(slice) } } @@ -28,3 +30,5 @@ unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() } + +const_assert!(mem::size_of::>() == mem::size_of::()); From bb07a4f9732cefadd9dfdceffc913bd26c92b5cd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 06:04:00 +0000 Subject: [PATCH 142/2232] Use qualified name of strlen --- diff --git a/src/cxx.cc b/src/cxx.cc index f55d062..6fae0f9 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -52,7 +52,7 @@ String::String(const std::string &s) { } String::String(const char *s) { - auto len = strlen(s); + auto len = std::strlen(s); if (!cxxbridge02$string$from(this, s, len)) { throw std::invalid_argument("data for rust::String is not utf-8"); } @@ -105,7 +105,7 @@ Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { } } -Str::Str(const char *s) : repr(Repr{s, strlen(s)}) { +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { throw std::invalid_argument("data for rust::Str is not utf-8"); } From 6cde49f694b293cabba4208dbacd4904cdf1e587 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 06:04:17 +0000 Subject: [PATCH 143/2232] Store language identifier inside ExternFn for diagnostics --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 5b51648..79a4a7b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -44,6 +44,7 @@ pub struct Struct { } pub struct ExternFn { + pub lang: Lang, pub doc: Doc, pub fn_token: Token![fn], pub ident: Ident, @@ -87,6 +88,12 @@ pub struct Ref { } #[derive(Copy, Clone, PartialEq)] +pub enum Lang { + Cxx, + Rust, +} + +#[derive(Copy, Clone, PartialEq)] pub enum Derive { Clone, Copy, diff --git a/syntax/parse.rs b/syntax/parse.rs index 7bae707..00ea15c 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,5 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Receiver, Ref, Struct, Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::quote; @@ -8,12 +8,6 @@ use syn::{ ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, }; -#[derive(Copy, Clone)] -enum Lang { - Cxx, - Rust, -} - pub fn parse_items(items: Vec) -> Result> { let mut apis = Vec::new(); for item in items { @@ -92,7 +86,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { items.push(api_type(ety)); } ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign)?; + let efn = parse_extern_fn(foreign, lang)?; items.push(api_function(efn)); } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -133,7 +127,7 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { }) } -fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { +fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -211,6 +205,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn) -> Result { let ident = foreign_fn.sig.ident.clone(); let semi_token = foreign_fn.semi_token; Ok(ExternFn { + lang, doc, fn_token, ident, From bb16d53057c9c87b0b79965f856176a154be73bc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 06:04:17 +0000 Subject: [PATCH 144/2232] Split the fallible function error message --- diff --git a/syntax/check.rs b/syntax/check.rs index 80937bb..4c2fff3 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Lang::*, Ref, Ty1, Type, Types, Var}; use proc_macro2::Ident; use syn::{Error, Result}; @@ -69,10 +69,16 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(return_by_value(ty, types)); } } - if efn.throws { + if efn.throws && efn.lang == Cxx { errors.push(Error::new_spanned( efn, - "fallible functions are not implemented yet", + "fallible C++ functions are not implemented yet", + )); + } + if efn.throws && efn.lang == Rust { + errors.push(Error::new_spanned( + efn, + "fallible Rust functions are not implemented yet", )); } } From 277e3ccdbada36096c6c0c0d40e01ff8d8806a61 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 07:11:01 +0000 Subject: [PATCH 145/2232] Factor out C++ indirect return predicate --- diff --git a/gen/write.rs b/gen/write.rs index 3cd3683..d57e21e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -187,10 +187,6 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { } fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let indirect_return = efn - .ret - .as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)); write_extern_return_type(out, &efn.ret, types); for name in out.namespace.clone() { write!(out, "{}$", name); @@ -205,6 +201,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write_extern_arg(out, arg, types); } + let indirect_return = indirect_return(efn, types); if indirect_return { if !efn.args.is_empty() { write!(out, ", "); @@ -285,11 +282,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write_extern_arg(out, arg, types); } - if efn - .ret - .as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)) - { + if indirect_return(efn, types) { if !efn.args.is_empty() { write!(out, ", "); } @@ -300,10 +293,6 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { } fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let indirect_return = efn - .ret - .as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)); for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -329,6 +318,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } write!(out, " "); + let indirect_return = indirect_return(efn, types); if indirect_return { write!(out, "::rust::MaybeUninit<"); write_type(out, efn.ret.as_ref().unwrap()); @@ -398,6 +388,12 @@ fn write_return_type(out: &mut OutFile, ty: &Option) { } } +fn indirect_return(efn: &ExternFn, types: &Types) -> bool { + efn.ret + .as_ref() + .map_or(false, |ret| types.needs_indirect_abi(ret)) +} + fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) { match ty { Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { From 1e548174c31a12f82defcfb9fd131a69c42a394d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 07:15:48 +0000 Subject: [PATCH 146/2232] Implement fallible Rust functions --- diff --git a/gen/write.rs b/gen/write.rs index d57e21e..ccb5a0f 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -271,7 +271,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - write_extern_return_type(out, &efn.ret, types); + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type(out, &efn.ret, types); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -305,7 +309,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type_space(out, &arg.ty); write!(out, "{}", arg.ident); } - write!(out, ") noexcept"); + write!(out, ")"); + if !efn.throws { + write!(out, " noexcept"); + } if out.header { writeln!(out, ";"); } else { @@ -339,6 +346,9 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { _ => {} } } + if efn.throws { + write!(out, "::rust::Str::Repr error$ = "); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -374,6 +384,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } writeln!(out, ";"); + if efn.throws { + writeln!(out, " if (error$.ptr) {{"); + writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " }}"); + } if indirect_return { writeln!(out, " return ::std::move(return$.value);"); } @@ -391,7 +406,7 @@ fn write_return_type(out: &mut OutFile, ty: &Option) { fn indirect_return(efn: &ExternFn, types: &Types) -> bool { efn.ret .as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)) + .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) } fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) { diff --git a/include/cxx.h b/include/cxx.h index e02145b..b48abb5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -138,6 +138,18 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +class Error final : std::exception { +public: + Error(const Error &); + Error(Error &&) noexcept; + Error(Str::Repr) noexcept; + ~Error() noexcept; + const char *what() const noexcept override; + +private: + Str::Repr msg; +}; + std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -145,6 +157,7 @@ std::ostream &operator<<(std::ostream &, const Str &); using string = String; using str = Str; template using box = Box; +using error = Error; struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fcfa1d9..defa565 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -136,7 +136,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types }); let ret = expand_extern_return_type(&efn.ret, types); let mut outparam = None; - if indirect_return(&efn.ret, types) { + if indirect_return(efn, types) { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } @@ -154,7 +154,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let decl = expand_cxx_function_decl(namespace, efn, types); let args = &efn.args; let ret = expand_return_type(&efn.ret); - let indirect_return = indirect_return(&efn.ret, types); + let indirect_return = indirect_return(efn, types); let vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { @@ -267,9 +267,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } }); let mut outparam = None; - let call = quote! { - ::cxx::private::catch_unwind(__fn, move || super::#ident(#(#vars),*)) - }; + let call = quote!(super::#ident(#(#vars),*)); let mut expr = efn .ret .as_ref() @@ -289,12 +287,22 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => None, }) .unwrap_or(call); - if indirect_return(&efn.ret, types) { + let indirect_return = indirect_return(efn, types); + if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); + } + if efn.throws { + expr = quote!(::cxx::private::r#try(__return, #expr)); + } else if indirect_return { expr = quote!(::std::ptr::write(__return, #expr)); } - let ret = expand_extern_return_type(&efn.ret, types); + expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); + let ret = if efn.throws { + quote!(-> ::std::option::Option<::cxx::private::RustStr>) + } else { + expand_extern_return_type(&efn.ret, types) + }; let link_name = format!("{}cxxbridge02${}", namespace, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); @@ -407,9 +415,10 @@ fn expand_return_type(ret: &Option) -> TokenStream { } } -fn indirect_return(ret: &Option, types: &Types) -> bool { - ret.as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)) +fn indirect_return(efn: &ExternFn, types: &Types) -> bool { + efn.ret + .as_ref() + .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) } fn expand_extern_type(ty: &Type) -> TokenStream { diff --git a/src/cxx.cc b/src/cxx.cc index 6fae0f9..0c1b8af 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -135,6 +135,32 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } +extern "C" { +const char *cxxbridge02$error(const char *ptr, size_t len) { + char *copy = new char[len]; + strncpy(copy, ptr, len); + return copy; +} +} // extern "C" + +Error::Error(Str::Repr msg) noexcept : msg(msg) {} + +Error::Error(const Error &other) { + this->msg.ptr = cxxbridge02$error(other.msg.ptr, other.msg.len); + this->msg.len = other.msg.len; +} + +Error::Error(Error &&other) noexcept { + delete[] this->msg.ptr; + this->msg = other.msg; + other.msg.ptr = nullptr; + other.msg.len = 0; +} + +Error::~Error() noexcept { delete[] this->msg.ptr; } + +const char *Error::what() const noexcept { return this->msg.ptr; } + } // namespace cxxbridge02 } // namespace rust diff --git a/src/exception.rs b/src/exception.rs new file mode 100644 index 0000000..3f32b45 --- /dev/null +++ b/src/exception.rs @@ -0,0 +1,35 @@ +use crate::rust_str::RustStr; +use std::fmt::Display; +use std::ptr; +use std::slice; +use std::str; + +pub unsafe fn r#try(ret: *mut T, result: Result) -> Option +where + E: Display, +{ + match result { + Ok(ok) => { + ptr::write(ret, ok); + None + } + Err(err) => Some(to_c_string(err.to_string())), + } +} + +unsafe fn to_c_string(msg: String) -> RustStr { + let mut msg = msg; + msg.as_mut_vec().push(b'\0'); + let ptr = msg.as_ptr(); + let len = msg.len(); + + extern "C" { + #[link_name = "cxxbridge02$error"] + fn error(ptr: *const u8, len: usize) -> *const u8; + } + + let copy = error(ptr, len); + let slice = slice::from_raw_parts(copy, len); + let string = str::from_utf8_unchecked(slice); + RustStr::from(string) +} diff --git a/src/lib.rs b/src/lib.rs index 3744bda..109090c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,6 +354,7 @@ mod assert; mod cxx_string; mod error; +mod exception; mod gen; mod opaque; mod paths; @@ -370,6 +371,7 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::exception::r#try; pub use crate::opaque::Opaque; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; diff --git a/syntax/check.rs b/syntax/check.rs index 4c2fff3..b781445 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -75,12 +75,6 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { "fallible C++ functions are not implemented yet", )); } - if efn.throws && efn.lang == Rust { - errors.push(Error::new_spanned( - efn, - "fallible Rust functions are not implemented yet", - )); - } } _ => {} } From b6c5ea72339b88c53bbddc542fbc657439f42d20 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 07:15:48 +0000 Subject: [PATCH 147/2232] Add Rust fallible test functions --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index c709678..07221c2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -1,4 +1,5 @@ use cxx::{CxxString, UniquePtr}; +use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] pub mod ffi { @@ -52,11 +53,25 @@ pub mod ffi { fn r_take_str(s: &str); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + + fn r_try_return_primitive() -> Result; + fn r_fail_return_primitive() -> Result; } } pub type R = usize; +#[derive(Debug)] +struct Error; + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("rust error") + } +} + fn r_return_primitive() -> usize { 2020 } @@ -131,3 +146,11 @@ fn r_take_rust_string(s: String) { fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } + +fn r_try_return_primitive() -> Result { + Ok(2020) +} + +fn r_fail_return_primitive() -> Result { + Err(Error) +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 350cd7d..0ec2658 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,6 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -126,6 +127,14 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + ASSERT(r_try_return_primitive() == 2020); + try { + r_fail_return_primitive(); + ASSERT(false); + } catch (const rust::Error &e) { + ASSERT(std::strcmp(e.what(), "rust error") == 0); + } + cxx_test_suite_set_correct(); return nullptr; } From 86b1723ed3695a3ebf1a27f160abafaa05f86efa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 08:11:47 +0000 Subject: [PATCH 148/2232] Avoid Option in ffi Option seems to work fine on rustc 1.43+ but not 1.42. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index defa565..9c198be 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -299,7 +299,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); let ret = if efn.throws { - quote!(-> ::std::option::Option<::cxx::private::RustStr>) + quote!(-> ::cxx::private::Error) } else { expand_extern_return_type(&efn.ret, types) }; diff --git a/src/exception.rs b/src/exception.rs index 3f32b45..57ff552 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,23 +1,29 @@ -use crate::rust_str::RustStr; use std::fmt::Display; use std::ptr; -use std::slice; -use std::str; -pub unsafe fn r#try(ret: *mut T, result: Result) -> Option +#[repr(C)] +pub struct Error { + ptr: *const u8, + len: usize, +} + +pub unsafe fn r#try(ret: *mut T, result: Result) -> Error where E: Display, { match result { Ok(ok) => { ptr::write(ret, ok); - None + Error { + ptr: ptr::null(), + len: 0, + } } - Err(err) => Some(to_c_string(err.to_string())), + Err(err) => to_c_string(err.to_string()), } } -unsafe fn to_c_string(msg: String) -> RustStr { +unsafe fn to_c_string(msg: String) -> Error { let mut msg = msg; msg.as_mut_vec().push(b'\0'); let ptr = msg.as_ptr(); @@ -29,7 +35,5 @@ unsafe fn to_c_string(msg: String) -> RustStr { } let copy = error(ptr, len); - let slice = slice::from_raw_parts(copy, len); - let string = str::from_utf8_unchecked(slice); - RustStr::from(string) + Error { ptr: copy, len } } diff --git a/src/lib.rs b/src/lib.rs index 109090c..aa7e84f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -371,7 +371,7 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { - pub use crate::exception::r#try; + pub use crate::exception::{r#try, Error}; pub use crate::opaque::Opaque; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; From 486b6ec592baba64ceefebd2237491053f2dda1d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 08:20:52 +0000 Subject: [PATCH 149/2232] Change fallible return type to Result union --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9c198be..b6bc099 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -299,7 +299,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); let ret = if efn.throws { - quote!(-> ::cxx::private::Error) + quote!(-> ::cxx::private::Result) } else { expand_extern_return_type(&efn.ret, types) }; diff --git a/src/exception.rs b/src/exception.rs deleted file mode 100644 index 57ff552..0000000 --- a/src/exception.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fmt::Display; -use std::ptr; - -#[repr(C)] -pub struct Error { - ptr: *const u8, - len: usize, -} - -pub unsafe fn r#try(ret: *mut T, result: Result) -> Error -where - E: Display, -{ - match result { - Ok(ok) => { - ptr::write(ret, ok); - Error { - ptr: ptr::null(), - len: 0, - } - } - Err(err) => to_c_string(err.to_string()), - } -} - -unsafe fn to_c_string(msg: String) -> Error { - let mut msg = msg; - msg.as_mut_vec().push(b'\0'); - let ptr = msg.as_ptr(); - let len = msg.len(); - - extern "C" { - #[link_name = "cxxbridge02$error"] - fn error(ptr: *const u8, len: usize) -> *const u8; - } - - let copy = error(ptr, len); - Error { ptr: copy, len } -} diff --git a/src/lib.rs b/src/lib.rs index aa7e84f..b985e1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,10 +354,10 @@ mod assert; mod cxx_string; mod error; -mod exception; mod gen; mod opaque; mod paths; +mod result; mod rust_str; mod rust_string; mod syntax; @@ -371,8 +371,8 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { - pub use crate::exception::{r#try, Error}; pub use crate::opaque::Opaque; + pub use crate::result::{r#try, Result}; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..6d04a2d --- /dev/null +++ b/src/result.rs @@ -0,0 +1,43 @@ +use crate::rust_str::RustStr; +use std::fmt::Display; +use std::ptr; +use std::result::Result as StdResult; +use std::slice; +use std::str; + +#[repr(C)] +pub union Result { + err: RustStr, + ok: *const u8, // null +} + +pub unsafe fn r#try(ret: *mut T, result: StdResult) -> Result +where + E: Display, +{ + match result { + Ok(ok) => { + ptr::write(ret, ok); + Result { ok: ptr::null() } + } + Err(err) => to_c_error(err.to_string()), + } +} + +unsafe fn to_c_error(msg: String) -> Result { + let mut msg = msg; + msg.as_mut_vec().push(b'\0'); + let ptr = msg.as_ptr(); + let len = msg.len(); + + extern "C" { + #[link_name = "cxxbridge02$error"] + fn error(ptr: *const u8, len: usize) -> *const u8; + } + + let copy = error(ptr, len); + let slice = slice::from_raw_parts(copy, len); + let string = str::from_utf8_unchecked(slice); + let err = RustStr::from(string); + Result { err } +} From 8c1a558b4f733ff191c00a7299f7fa61bbf2b465 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 08:34:31 +0000 Subject: [PATCH 150/2232] Merge pull request #73 from dtolnay/result Implement conversion of Result⟶exception --- diff --git a/gen/write.rs b/gen/write.rs index d57e21e..ccb5a0f 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -271,7 +271,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - write_extern_return_type(out, &efn.ret, types); + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type(out, &efn.ret, types); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -305,7 +309,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type_space(out, &arg.ty); write!(out, "{}", arg.ident); } - write!(out, ") noexcept"); + write!(out, ")"); + if !efn.throws { + write!(out, " noexcept"); + } if out.header { writeln!(out, ";"); } else { @@ -339,6 +346,9 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { _ => {} } } + if efn.throws { + write!(out, "::rust::Str::Repr error$ = "); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -374,6 +384,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } writeln!(out, ";"); + if efn.throws { + writeln!(out, " if (error$.ptr) {{"); + writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " }}"); + } if indirect_return { writeln!(out, " return ::std::move(return$.value);"); } @@ -391,7 +406,7 @@ fn write_return_type(out: &mut OutFile, ty: &Option) { fn indirect_return(efn: &ExternFn, types: &Types) -> bool { efn.ret .as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)) + .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) } fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) { diff --git a/include/cxx.h b/include/cxx.h index e02145b..b48abb5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -138,6 +138,18 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +class Error final : std::exception { +public: + Error(const Error &); + Error(Error &&) noexcept; + Error(Str::Repr) noexcept; + ~Error() noexcept; + const char *what() const noexcept override; + +private: + Str::Repr msg; +}; + std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -145,6 +157,7 @@ std::ostream &operator<<(std::ostream &, const Str &); using string = String; using str = Str; template using box = Box; +using error = Error; struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fcfa1d9..b6bc099 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -136,7 +136,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types }); let ret = expand_extern_return_type(&efn.ret, types); let mut outparam = None; - if indirect_return(&efn.ret, types) { + if indirect_return(efn, types) { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } @@ -154,7 +154,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let decl = expand_cxx_function_decl(namespace, efn, types); let args = &efn.args; let ret = expand_return_type(&efn.ret); - let indirect_return = indirect_return(&efn.ret, types); + let indirect_return = indirect_return(efn, types); let vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { @@ -267,9 +267,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } }); let mut outparam = None; - let call = quote! { - ::cxx::private::catch_unwind(__fn, move || super::#ident(#(#vars),*)) - }; + let call = quote!(super::#ident(#(#vars),*)); let mut expr = efn .ret .as_ref() @@ -289,12 +287,22 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => None, }) .unwrap_or(call); - if indirect_return(&efn.ret, types) { + let indirect_return = indirect_return(efn, types); + if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); + } + if efn.throws { + expr = quote!(::cxx::private::r#try(__return, #expr)); + } else if indirect_return { expr = quote!(::std::ptr::write(__return, #expr)); } - let ret = expand_extern_return_type(&efn.ret, types); + expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); + let ret = if efn.throws { + quote!(-> ::cxx::private::Result) + } else { + expand_extern_return_type(&efn.ret, types) + }; let link_name = format!("{}cxxbridge02${}", namespace, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); @@ -407,9 +415,10 @@ fn expand_return_type(ret: &Option) -> TokenStream { } } -fn indirect_return(ret: &Option, types: &Types) -> bool { - ret.as_ref() - .map_or(false, |ret| types.needs_indirect_abi(ret)) +fn indirect_return(efn: &ExternFn, types: &Types) -> bool { + efn.ret + .as_ref() + .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) } fn expand_extern_type(ty: &Type) -> TokenStream { diff --git a/src/cxx.cc b/src/cxx.cc index 6fae0f9..0c1b8af 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -135,6 +135,32 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } +extern "C" { +const char *cxxbridge02$error(const char *ptr, size_t len) { + char *copy = new char[len]; + strncpy(copy, ptr, len); + return copy; +} +} // extern "C" + +Error::Error(Str::Repr msg) noexcept : msg(msg) {} + +Error::Error(const Error &other) { + this->msg.ptr = cxxbridge02$error(other.msg.ptr, other.msg.len); + this->msg.len = other.msg.len; +} + +Error::Error(Error &&other) noexcept { + delete[] this->msg.ptr; + this->msg = other.msg; + other.msg.ptr = nullptr; + other.msg.len = 0; +} + +Error::~Error() noexcept { delete[] this->msg.ptr; } + +const char *Error::what() const noexcept { return this->msg.ptr; } + } // namespace cxxbridge02 } // namespace rust diff --git a/src/lib.rs b/src/lib.rs index 3744bda..b985e1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,6 +357,7 @@ mod error; mod gen; mod opaque; mod paths; +mod result; mod rust_str; mod rust_string; mod syntax; @@ -371,6 +372,7 @@ pub use cxxbridge_macro::bridge; #[doc(hidden)] pub mod private { pub use crate::opaque::Opaque; + pub use crate::result::{r#try, Result}; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..6d04a2d --- /dev/null +++ b/src/result.rs @@ -0,0 +1,43 @@ +use crate::rust_str::RustStr; +use std::fmt::Display; +use std::ptr; +use std::result::Result as StdResult; +use std::slice; +use std::str; + +#[repr(C)] +pub union Result { + err: RustStr, + ok: *const u8, // null +} + +pub unsafe fn r#try(ret: *mut T, result: StdResult) -> Result +where + E: Display, +{ + match result { + Ok(ok) => { + ptr::write(ret, ok); + Result { ok: ptr::null() } + } + Err(err) => to_c_error(err.to_string()), + } +} + +unsafe fn to_c_error(msg: String) -> Result { + let mut msg = msg; + msg.as_mut_vec().push(b'\0'); + let ptr = msg.as_ptr(); + let len = msg.len(); + + extern "C" { + #[link_name = "cxxbridge02$error"] + fn error(ptr: *const u8, len: usize) -> *const u8; + } + + let copy = error(ptr, len); + let slice = slice::from_raw_parts(copy, len); + let string = str::from_utf8_unchecked(slice); + let err = RustStr::from(string); + Result { err } +} diff --git a/syntax/check.rs b/syntax/check.rs index 4c2fff3..b781445 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -75,12 +75,6 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { "fallible C++ functions are not implemented yet", )); } - if efn.throws && efn.lang == Rust { - errors.push(Error::new_spanned( - efn, - "fallible Rust functions are not implemented yet", - )); - } } _ => {} } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index c709678..07221c2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -1,4 +1,5 @@ use cxx::{CxxString, UniquePtr}; +use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] pub mod ffi { @@ -52,11 +53,25 @@ pub mod ffi { fn r_take_str(s: &str); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + + fn r_try_return_primitive() -> Result; + fn r_fail_return_primitive() -> Result; } } pub type R = usize; +#[derive(Debug)] +struct Error; + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("rust error") + } +} + fn r_return_primitive() -> usize { 2020 } @@ -131,3 +146,11 @@ fn r_take_rust_string(s: String) { fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } + +fn r_try_return_primitive() -> Result { + Ok(2020) +} + +fn r_fail_return_primitive() -> Result { + Err(Error) +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 350cd7d..0ec2658 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,6 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -126,6 +127,14 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + ASSERT(r_try_return_primitive() == 2020); + try { + r_fail_return_primitive(); + ASSERT(false); + } catch (const rust::Error &e) { + ASSERT(std::strcmp(e.what(), "rust error") == 0); + } + cxx_test_suite_set_correct(); return nullptr; } From cecada6d383ed2b42943cfa65b0ebd6c32fb78c1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 08:46:07 +0000 Subject: [PATCH 151/2232] Fix fallible void return --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index b6bc099..88fc812 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -293,7 +293,11 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type outparam = Some(quote!(__return: *mut #ret)); } if efn.throws { - expr = quote!(::cxx::private::r#try(__return, #expr)); + let out = match efn.ret { + Some(_) => quote!(__return), + None => quote!(&mut ()), + }; + expr = quote!(::cxx::private::r#try(#out, #expr)); } else if indirect_return { expr = quote!(::std::ptr::write(__return, #expr)); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 07221c2..fd54654 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -54,6 +54,7 @@ pub mod ffi { fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; fn r_fail_return_primitive() -> Result; } @@ -147,6 +148,10 @@ fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } +fn r_try_return_void() -> Result<(), Error> { + Ok(()) +} + fn r_try_return_primitive() -> Result { Ok(2020) } From 13af5ccb767a0c9cd3ecbe563e1564af9f5c5131 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 17 2020 20:29:06 +0000 Subject: [PATCH 152/2232] Select a single docs.rs build target --- diff --git a/Cargo.toml b/Cargo.toml index f81cdb9..8432bc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,6 @@ trybuild = "1.0.21" [workspace] members = ["cmd", "demo-rs", "macro", "tests/ffi"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 4e83b81..7ee974c 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -23,3 +23,6 @@ quote = "1.0" structopt = "0.3" syn = { version = "1.0", features = ["full"] } thiserror = "1.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 707a19b..635d7cc 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -20,3 +20,6 @@ syn = { version = "1.0", features = ["full"] } [dev-dependencies] cxx = { version = "0.2", path = ".." } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] From ebef4a23a2dcf92f30eb8ce14a1275201a4a9a53 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 00:34:52 +0000 Subject: [PATCH 153/2232] Implement fallible C++ functions --- diff --git a/gen/include.rs b/gen/include.rs index a4b416a..da6678d 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -18,6 +18,7 @@ pub fn get(guard: &str) -> &'static str { pub struct Includes { custom: Vec, pub cstdint: bool, + pub cstring: bool, pub memory: bool, pub string: bool, pub type_traits: bool, @@ -41,6 +42,9 @@ impl Display for Includes { if self.cstdint { writeln!(f, "#include ")?; } + if self.cstring { + writeln!(f, "#include ")?; + } if self.memory { writeln!(f, "#include ")?; } diff --git a/gen/write.rs b/gen/write.rs index ccb5a0f..3398f9a 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -45,6 +45,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b if !header { out.begin_block("extern \"C\""); + write_exception_glue(out, apis); for api in apis { let (efn, write): (_, fn(_, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), @@ -186,8 +187,32 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } +fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { + let mut has_cxx_throws = false; + for api in apis { + if let Api::CxxFunction(efn) = api { + if efn.throws { + has_cxx_throws = true; + break; + } + } + } + + if has_cxx_throws { + out.next_section(); + write!( + out, + "const char *cxxbridge02$exception(const char *, size_t);", + ); + } +} + fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - write_extern_return_type(out, &efn.ret, types); + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type(out, &efn.ret, types); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -221,6 +246,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } writeln!(out, ") = {};", efn.ident); write!(out, " "); + if efn.throws { + writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, " try {{"); + write!(out, " "); + } if indirect_return { write!(out, "new (return$) "); write_type(out, efn.ret.as_ref().unwrap()); @@ -267,6 +297,19 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, ")"); } writeln!(out, ";"); + if efn.throws { + out.include.cstring = true; + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }} catch (const ::std::exception &catch$) {{"); + writeln!(out, " const char *return$ = catch$.what();"); + writeln!(out, " throw$.len = ::std::strlen(return$);"); + writeln!( + out, + " throw$.ptr = cxxbridge02$exception(return$, throw$.len);", + ); + writeln!(out, " }}"); + writeln!(out, " return throw$;"); + } writeln!(out, "}}"); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 88fc812..e11b2d3 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -134,7 +134,11 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types quote!(#ident: #ty) } }); - let ret = expand_extern_return_type(&efn.ret, types); + let ret = if efn.throws { + quote!(-> ::cxx::private::Result) + } else { + expand_extern_return_type(&efn.ret, types) + }; let mut outparam = None; if indirect_return(efn, types) { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); @@ -153,7 +157,15 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); let args = &efn.args; - let ret = expand_return_type(&efn.ret); + let ret = if efn.throws { + let ok = match &efn.ret { + Some(ret) => quote!(#ret), + None => quote!(()), + }; + quote!(-> ::std::result::Result<#ok, ::cxx::Exception>) + } else { + expand_return_type(&efn.ret) + }; let indirect_return = indirect_return(efn, types); let vars = efn.args.iter().map(|arg| { let var = &arg.ident; @@ -192,10 +204,21 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); setup.extend(quote! { let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit(); - #local_name(#(#vars,)* __return.as_mut_ptr()); }); + if efn.throws { + setup.extend(quote! { + #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?; + }); + quote!(::std::result::Result::Ok(__return.assume_init())) + } else { + setup.extend(quote! { + #local_name(#(#vars,)* __return.as_mut_ptr()); + }); + quote!(__return.assume_init()) + } + } else if efn.throws { quote! { - __return.assume_init() + #local_name(#(#vars),*).exception() } } else { quote! { diff --git a/src/exception.rs b/src/exception.rs new file mode 100644 index 0000000..c436196 --- /dev/null +++ b/src/exception.rs @@ -0,0 +1,29 @@ +use std::fmt::{self, Debug, Display}; +use std::slice; + +/// Exception thrown from an `extern "C"` function. +#[derive(Debug)] +pub struct Exception { + pub(crate) what: Box, +} + +impl Display for Exception { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.what) + } +} + +impl std::error::Error for Exception {} + +impl Exception { + pub fn what(&self) -> &str { + &self.what + } +} + +#[export_name = "cxxbridge02$exception"] +unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { + let slice = slice::from_raw_parts(ptr, len); + let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); + Box::leak(boxed).as_ptr() +} diff --git a/src/lib.rs b/src/lib.rs index b985e1d..5dd2649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,6 +354,7 @@ mod assert; mod cxx_string; mod error; +mod exception; mod gen; mod opaque; mod paths; @@ -365,6 +366,7 @@ mod unique_ptr; mod unwind; pub use crate::cxx_string::CxxString; +pub use crate::exception::Exception; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; diff --git a/src/result.rs b/src/result.rs index 6d04a2d..047260b 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,5 +1,7 @@ +use crate::exception::Exception; use crate::rust_str::RustStr; use std::fmt::Display; +use std::mem; use std::ptr; use std::result::Result as StdResult; use std::slice; @@ -41,3 +43,18 @@ unsafe fn to_c_error(msg: String) -> Result { let err = RustStr::from(string); Result { err } } + +impl Result { + pub unsafe fn exception(self) -> StdResult<(), Exception> { + if self.ok.is_null() { + Ok(()) + } else { + let err = self.err; + let slice = slice::from_raw_parts(err.ptr.as_ptr(), err.len); + let s = str::from_utf8_unchecked(slice); + Err(Exception { + what: mem::transmute::<*const str, Box>(s), + }) + } + } +} diff --git a/src/rust_str.rs b/src/rust_str.rs index 110889b..51e4835 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -7,8 +7,8 @@ use std::str; #[repr(C)] #[derive(Copy, Clone)] pub struct RustStr { - ptr: NonNull, - len: usize, + pub(crate) ptr: NonNull, + pub(crate) len: usize, } impl RustStr { diff --git a/syntax/check.rs b/syntax/check.rs index b781445..ef3faff 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Lang::*, Ref, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; use proc_macro2::Ident; use syn::{Error, Result}; @@ -69,12 +69,6 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(return_by_value(ty, types)); } } - if efn.throws && efn.lang == Cxx { - errors.push(Error::new_spanned( - efn, - "fallible C++ functions are not implemented yet", - )); - } } _ => {} } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fd54654..fe6a364 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -30,6 +30,10 @@ pub mod ffi { fn c_take_str(s: &str); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + + fn c_try_return_void() -> Result<()>; + fn c_try_return_primitive() -> Result; + fn c_fail_return_primitive() -> Result; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 0ec2658..4f36f9b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,6 +1,7 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" #include +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -91,6 +92,12 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_try_return_void() {} + +size_t c_try_return_primitive() { return 2020; } + +size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7cff6fa..6a5f802 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -36,4 +36,8 @@ void c_take_str(rust::Str s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_try_return_void(); +size_t c_try_return_primitive(); +size_t c_fail_return_primitive(); + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index a30aefb..d86ebd2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -38,6 +38,13 @@ fn test_c_return() { .to_str() .unwrap() ); + + assert_eq!((), ffi::c_try_return_void().unwrap()); + assert_eq!(2020, ffi::c_try_return_primitive().unwrap()); + assert_eq!( + "logic error", + ffi::c_fail_return_primitive().unwrap_err().what(), + ); } #[test] From a56c07053efa5616c574ad29b1c3d424a6dbeeb2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 00:47:21 +0000 Subject: [PATCH 154/2232] Merge pull request #74 from dtolnay/result Implement conversion of exception⟶Result --- diff --git a/gen/include.rs b/gen/include.rs index a4b416a..da6678d 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -18,6 +18,7 @@ pub fn get(guard: &str) -> &'static str { pub struct Includes { custom: Vec, pub cstdint: bool, + pub cstring: bool, pub memory: bool, pub string: bool, pub type_traits: bool, @@ -41,6 +42,9 @@ impl Display for Includes { if self.cstdint { writeln!(f, "#include ")?; } + if self.cstring { + writeln!(f, "#include ")?; + } if self.memory { writeln!(f, "#include ")?; } diff --git a/gen/write.rs b/gen/write.rs index ccb5a0f..3398f9a 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -45,6 +45,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b if !header { out.begin_block("extern \"C\""); + write_exception_glue(out, apis); for api in apis { let (efn, write): (_, fn(_, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), @@ -186,8 +187,32 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } +fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { + let mut has_cxx_throws = false; + for api in apis { + if let Api::CxxFunction(efn) = api { + if efn.throws { + has_cxx_throws = true; + break; + } + } + } + + if has_cxx_throws { + out.next_section(); + write!( + out, + "const char *cxxbridge02$exception(const char *, size_t);", + ); + } +} + fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - write_extern_return_type(out, &efn.ret, types); + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type(out, &efn.ret, types); + } for name in out.namespace.clone() { write!(out, "{}$", name); } @@ -221,6 +246,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } writeln!(out, ") = {};", efn.ident); write!(out, " "); + if efn.throws { + writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, " try {{"); + write!(out, " "); + } if indirect_return { write!(out, "new (return$) "); write_type(out, efn.ret.as_ref().unwrap()); @@ -267,6 +297,19 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, ")"); } writeln!(out, ";"); + if efn.throws { + out.include.cstring = true; + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }} catch (const ::std::exception &catch$) {{"); + writeln!(out, " const char *return$ = catch$.what();"); + writeln!(out, " throw$.len = ::std::strlen(return$);"); + writeln!( + out, + " throw$.ptr = cxxbridge02$exception(return$, throw$.len);", + ); + writeln!(out, " }}"); + writeln!(out, " return throw$;"); + } writeln!(out, "}}"); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 88fc812..e11b2d3 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -134,7 +134,11 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types quote!(#ident: #ty) } }); - let ret = expand_extern_return_type(&efn.ret, types); + let ret = if efn.throws { + quote!(-> ::cxx::private::Result) + } else { + expand_extern_return_type(&efn.ret, types) + }; let mut outparam = None; if indirect_return(efn, types) { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); @@ -153,7 +157,15 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); let args = &efn.args; - let ret = expand_return_type(&efn.ret); + let ret = if efn.throws { + let ok = match &efn.ret { + Some(ret) => quote!(#ret), + None => quote!(()), + }; + quote!(-> ::std::result::Result<#ok, ::cxx::Exception>) + } else { + expand_return_type(&efn.ret) + }; let indirect_return = indirect_return(efn, types); let vars = efn.args.iter().map(|arg| { let var = &arg.ident; @@ -192,10 +204,21 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); setup.extend(quote! { let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit(); - #local_name(#(#vars,)* __return.as_mut_ptr()); }); + if efn.throws { + setup.extend(quote! { + #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?; + }); + quote!(::std::result::Result::Ok(__return.assume_init())) + } else { + setup.extend(quote! { + #local_name(#(#vars,)* __return.as_mut_ptr()); + }); + quote!(__return.assume_init()) + } + } else if efn.throws { quote! { - __return.assume_init() + #local_name(#(#vars),*).exception() } } else { quote! { diff --git a/src/exception.rs b/src/exception.rs new file mode 100644 index 0000000..c436196 --- /dev/null +++ b/src/exception.rs @@ -0,0 +1,29 @@ +use std::fmt::{self, Debug, Display}; +use std::slice; + +/// Exception thrown from an `extern "C"` function. +#[derive(Debug)] +pub struct Exception { + pub(crate) what: Box, +} + +impl Display for Exception { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.what) + } +} + +impl std::error::Error for Exception {} + +impl Exception { + pub fn what(&self) -> &str { + &self.what + } +} + +#[export_name = "cxxbridge02$exception"] +unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { + let slice = slice::from_raw_parts(ptr, len); + let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); + Box::leak(boxed).as_ptr() +} diff --git a/src/lib.rs b/src/lib.rs index b985e1d..5dd2649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,6 +354,7 @@ mod assert; mod cxx_string; mod error; +mod exception; mod gen; mod opaque; mod paths; @@ -365,6 +366,7 @@ mod unique_ptr; mod unwind; pub use crate::cxx_string::CxxString; +pub use crate::exception::Exception; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; diff --git a/src/result.rs b/src/result.rs index 6d04a2d..047260b 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,5 +1,7 @@ +use crate::exception::Exception; use crate::rust_str::RustStr; use std::fmt::Display; +use std::mem; use std::ptr; use std::result::Result as StdResult; use std::slice; @@ -41,3 +43,18 @@ unsafe fn to_c_error(msg: String) -> Result { let err = RustStr::from(string); Result { err } } + +impl Result { + pub unsafe fn exception(self) -> StdResult<(), Exception> { + if self.ok.is_null() { + Ok(()) + } else { + let err = self.err; + let slice = slice::from_raw_parts(err.ptr.as_ptr(), err.len); + let s = str::from_utf8_unchecked(slice); + Err(Exception { + what: mem::transmute::<*const str, Box>(s), + }) + } + } +} diff --git a/src/rust_str.rs b/src/rust_str.rs index 110889b..51e4835 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -7,8 +7,8 @@ use std::str; #[repr(C)] #[derive(Copy, Clone)] pub struct RustStr { - ptr: NonNull, - len: usize, + pub(crate) ptr: NonNull, + pub(crate) len: usize, } impl RustStr { diff --git a/syntax/check.rs b/syntax/check.rs index b781445..ef3faff 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Lang::*, Ref, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; use proc_macro2::Ident; use syn::{Error, Result}; @@ -69,12 +69,6 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(return_by_value(ty, types)); } } - if efn.throws && efn.lang == Cxx { - errors.push(Error::new_spanned( - efn, - "fallible C++ functions are not implemented yet", - )); - } } _ => {} } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fd54654..fe6a364 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -30,6 +30,10 @@ pub mod ffi { fn c_take_str(s: &str); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + + fn c_try_return_void() -> Result<()>; + fn c_try_return_primitive() -> Result; + fn c_fail_return_primitive() -> Result; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 0ec2658..4f36f9b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,6 +1,7 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs" #include +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -91,6 +92,12 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_try_return_void() {} + +size_t c_try_return_primitive() { return 2020; } + +size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7cff6fa..6a5f802 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -36,4 +36,8 @@ void c_take_str(rust::Str s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_try_return_void(); +size_t c_try_return_primitive(); +size_t c_fail_return_primitive(); + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index a30aefb..d86ebd2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -38,6 +38,13 @@ fn test_c_return() { .to_str() .unwrap() ); + + assert_eq!((), ffi::c_try_return_void().unwrap()); + assert_eq!(2020, ffi::c_try_return_primitive().unwrap()); + assert_eq!( + "logic error", + ffi::c_fail_return_primitive().unwrap_err().what(), + ); } #[test] From bffcdd5f4bdf47c691770d7de7e971f7ba220172 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 02:12:38 +0000 Subject: [PATCH 155/2232] Use consistent filenames for generated code This makes it clearer that the filename doesn't need to be connected to the rule name. --- diff --git a/demo-rs/BUCK b/demo-rs/BUCK index a0f6fb0..eac4182 100644 --- a/demo-rs/BUCK +++ b/demo-rs/BUCK @@ -22,7 +22,7 @@ genrule( srcs = ["src/main.rs"], cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", type = "cxxbridge", - out = "gen-demo.h", + out = "generated.h", ) genrule( @@ -30,7 +30,7 @@ genrule( srcs = ["src/main.rs"], cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", type = "cxxbridge", - out = "gen-demo.cc", + out = "generated.cc", ) cxx_library( diff --git a/demo-rs/BUILD b/demo-rs/BUILD index 389ee58..f703be3 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -30,7 +30,7 @@ genrule( genrule( name = "gen-source", srcs = ["src/main.rs"], - outs = ["gen-demo.cc"], + outs = ["generated.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) diff --git a/tests/BUCK b/tests/BUCK index 81f74de..b488c90 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -31,12 +31,12 @@ genrule( name = "gen-header", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - out = "gen.h", + out = "generated.h", ) genrule( name = "gen-source", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - out = "gen.cc", + out = "generated.cc", ) diff --git a/tests/BUILD b/tests/BUILD index 16825f6..ef6477f 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -39,7 +39,7 @@ genrule( genrule( name = "gen-source", srcs = ["ffi/lib.rs"], - outs = ["gen.cc"], + outs = ["generated.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) From 1a2683ac220706c26ca80cf6c679793aedad9d35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 02:13:22 +0000 Subject: [PATCH 156/2232] Allow .rs.h extension when including generated header --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 04287b8..cd447ea 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -1,5 +1,5 @@ #include "demo-cxx/demo.h" -#include "demo-rs/src/main.rs" +#include "demo-rs/src/main.rs.h" #include namespace org { diff --git a/demo-rs/BUCK b/demo-rs/BUCK index eac4182..d4164d3 100644 --- a/demo-rs/BUCK +++ b/demo-rs/BUCK @@ -36,7 +36,7 @@ genrule( cxx_library( name = "include", exported_headers = { - "src/main.rs": ":gen-header", + "src/main.rs.h": ":gen-header", }, visibility = ["PUBLIC"], ) diff --git a/demo-rs/BUILD b/demo-rs/BUILD index f703be3..e3ebb96 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -22,7 +22,7 @@ cc_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - outs = ["main.rs"], + outs = ["main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) diff --git a/src/paths.rs b/src/paths.rs index e318664..62fbb6e 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -22,21 +22,28 @@ fn try_cc_build() -> Result { } // Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.h. +// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. pub(crate) fn symlink_header(path: &Path, original: &Path) { let _ = try_symlink_header(path, original); } fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { + #[cfg(unix)] + use os::unix::fs::symlink; + #[cfg(windows)] + use os::windows::fs::symlink_file as symlink; + let suffix = relative_to_parent_of_target_dir(original)?; let ref dst = include_dir()?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); - #[cfg(unix)] - os::unix::fs::symlink(path, dst)?; - #[cfg(windows)] - os::windows::fs::symlink_file(path, dst)?; + symlink(path, dst)?; + + let mut file_name = dst.file_name().unwrap().to_os_string(); + file_name.push(".h"); + let dst2 = dst.with_file_name(file_name); + symlink(path, dst2)?; Ok(()) } diff --git a/tests/BUCK b/tests/BUCK index b488c90..659223c 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -21,7 +21,7 @@ cxx_library( ":gen-source", ], headers = { - "ffi/lib.rs": ":gen-header", + "ffi/lib.rs.h": ":gen-header", "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], diff --git a/tests/BUILD b/tests/BUILD index ef6477f..65c5b41 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -31,7 +31,7 @@ cc_library( genrule( name = "gen-header", srcs = ["ffi/lib.rs"], - outs = ["lib.rs"], + outs = ["lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4f36f9b..184e8aa 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ #include "tests/ffi/tests.h" -#include "tests/ffi/lib.rs" +#include "tests/ffi/lib.rs.h" #include #include From 5840afe465ded5d8415d015d0467b23369234b51 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 02:41:13 +0000 Subject: [PATCH 157/2232] Merge pull request #75 from dtolnay/extension Allow .rs.h extension when including generated header --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 04287b8..cd447ea 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -1,5 +1,5 @@ #include "demo-cxx/demo.h" -#include "demo-rs/src/main.rs" +#include "demo-rs/src/main.rs.h" #include namespace org { diff --git a/demo-rs/BUCK b/demo-rs/BUCK index eac4182..d4164d3 100644 --- a/demo-rs/BUCK +++ b/demo-rs/BUCK @@ -36,7 +36,7 @@ genrule( cxx_library( name = "include", exported_headers = { - "src/main.rs": ":gen-header", + "src/main.rs.h": ":gen-header", }, visibility = ["PUBLIC"], ) diff --git a/demo-rs/BUILD b/demo-rs/BUILD index f703be3..e3ebb96 100644 --- a/demo-rs/BUILD +++ b/demo-rs/BUILD @@ -22,7 +22,7 @@ cc_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - outs = ["main.rs"], + outs = ["main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) diff --git a/src/paths.rs b/src/paths.rs index e318664..62fbb6e 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -22,21 +22,28 @@ fn try_cc_build() -> Result { } // Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.h. +// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. pub(crate) fn symlink_header(path: &Path, original: &Path) { let _ = try_symlink_header(path, original); } fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { + #[cfg(unix)] + use os::unix::fs::symlink; + #[cfg(windows)] + use os::windows::fs::symlink_file as symlink; + let suffix = relative_to_parent_of_target_dir(original)?; let ref dst = include_dir()?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); - #[cfg(unix)] - os::unix::fs::symlink(path, dst)?; - #[cfg(windows)] - os::windows::fs::symlink_file(path, dst)?; + symlink(path, dst)?; + + let mut file_name = dst.file_name().unwrap().to_os_string(); + file_name.push(".h"); + let dst2 = dst.with_file_name(file_name); + symlink(path, dst2)?; Ok(()) } diff --git a/tests/BUCK b/tests/BUCK index b488c90..659223c 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -21,7 +21,7 @@ cxx_library( ":gen-source", ], headers = { - "ffi/lib.rs": ":gen-header", + "ffi/lib.rs.h": ":gen-header", "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], diff --git a/tests/BUILD b/tests/BUILD index ef6477f..65c5b41 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -31,7 +31,7 @@ cc_library( genrule( name = "gen-header", srcs = ["ffi/lib.rs"], - outs = ["lib.rs"], + outs = ["lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4f36f9b..184e8aa 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ #include "tests/ffi/tests.h" -#include "tests/ffi/lib.rs" +#include "tests/ffi/lib.rs.h" #include #include From 26ad0bd98c23c5983c7d13df0ae3ec3b578e9b6c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 04:34:16 +0000 Subject: [PATCH 158/2232] Look for end of line when finding headers ifdefs --- diff --git a/gen/include.rs b/gen/include.rs index da6678d..68cb00d 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -5,8 +5,8 @@ pub static HEADER: &str = include_str!("include/cxx.h"); pub fn get(guard: &str) -> &'static str { let ifndef = format!("#ifndef {}", guard); let endif = format!("#endif // {}", guard); - let begin = HEADER.find(&ifndef); - let end = HEADER.find(&endif); + let begin = find_line(&ifndef); + let end = find_line(&endif); if let (Some(begin), Some(end)) = (begin, end) { &HEADER[begin..end + endif.len()] } else { @@ -14,6 +14,18 @@ pub fn get(guard: &str) -> &'static str { } } +fn find_line(line: &str) -> Option { + let mut offset = 0; + loop { + offset += HEADER[offset..].find(line)?; + let rest = &HEADER[offset + line.len()..]; + if rest.starts_with('\n') || rest.starts_with('\r') { + return Some(offset); + } + offset += line.len(); + } +} + #[derive(Default)] pub struct Includes { custom: Vec, From b7a7cb6785b63188c411a74db3f1eb05dce240b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 04:40:47 +0000 Subject: [PATCH 159/2232] Provide more struct definitions where needed --- diff --git a/gen/include.rs b/gen/include.rs index 68cb00d..a7657e4 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -29,8 +29,10 @@ fn find_line(line: &str) -> Option { #[derive(Default)] pub struct Includes { custom: Vec, + pub array: bool, pub cstdint: bool, pub cstring: bool, + pub exception: bool, pub memory: bool, pub string: bool, pub type_traits: bool, @@ -51,12 +53,18 @@ impl Display for Includes { for include in &self.custom { writeln!(f, "#include \"{}\"", include.escape_default())?; } + if self.array { + writeln!(f, "#include ")?; + } if self.cstdint { writeln!(f, "#include ")?; } if self.cstring { writeln!(f, "#include ")?; } + if self.exception { + writeln!(f, "#include ")?; + } if self.memory { writeln!(f, "#include ")?; } diff --git a/gen/write.rs b/gen/write.rs index 3398f9a..a75d4c0 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -97,47 +97,84 @@ fn write_includes(out: &mut OutFile, types: &Types) { } fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { + let mut needs_rust_string = false; + let mut needs_rust_str = false; let mut needs_rust_box = false; for ty in types { - if let Type::RustBox(_) = ty { - needs_rust_box = true; - break; + match ty { + Type::RustBox(_) => { + out.include.type_traits = true; + needs_rust_box = true; + } + Type::Str(_) => { + out.include.cstdint = true; + out.include.string = true; + needs_rust_str = true; + } + ty if ty == RustString => { + out.include.array = true; + out.include.cstdint = true; + out.include.string = true; + needs_rust_string = true; + } + _ => {} } } + let mut needs_rust_error = false; + let mut needs_unsafe_bitcopy = false; let mut needs_manually_drop = false; let mut needs_maybe_uninit = false; for api in apis { - if let Api::RustFunction(efn) = api { - for arg in &efn.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - needs_manually_drop = true; - break; + match api { + Api::CxxFunction(efn) if !out.header => { + for arg in &efn.args { + if arg.ty == RustString { + needs_unsafe_bitcopy = true; + break; + } } } - if let Some(ret) = &efn.ret { - if types.needs_indirect_abi(ret) { - needs_maybe_uninit = true; + Api::RustFunction(efn) if !out.header => { + if efn.throws { + out.include.exception = true; + needs_rust_error = true; + } + for arg in &efn.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + needs_manually_drop = true; + break; + } + } + if let Some(ret) = &efn.ret { + if types.needs_indirect_abi(ret) { + needs_maybe_uninit = true; + } } } + _ => {} } } out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge02"); - if needs_rust_box || needs_manually_drop || needs_maybe_uninit { + if needs_rust_string + || needs_rust_str + || needs_rust_box + || needs_rust_error + || needs_unsafe_bitcopy + || needs_manually_drop + || needs_maybe_uninit + { writeln!(out, "// #include \"rust/cxx.h\""); } - if needs_rust_box { - out.next_section(); - for line in include::get("CXXBRIDGE02_RUST_BOX").lines() { - if !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - } + write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); + write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); + write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); @@ -166,6 +203,17 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } +fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { + if needed { + out.next_section(); + for line in include::get(section).lines() { + if !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + } +} + fn write_struct(out: &mut OutFile, strct: &Struct) { for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); diff --git a/include/cxx.h b/include/cxx.h index b48abb5..a021ca7 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -10,6 +11,8 @@ inline namespace cxxbridge02 { struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_STRING +#define CXXBRIDGE02_RUST_STRING class String final { public: String() noexcept; @@ -37,7 +40,10 @@ private: // Size and alignment statically verified by rust_string.rs. std::array repr; }; +#endif // CXXBRIDGE02_RUST_STRING +#ifndef CXXBRIDGE02_RUST_STR +#define CXXBRIDGE02_RUST_STR class Str final { public: Str() noexcept; @@ -70,6 +76,7 @@ public: private: Repr repr; }; +#endif // CXXBRIDGE02_RUST_STR #ifndef CXXBRIDGE02_RUST_BOX #define CXXBRIDGE02_RUST_BOX @@ -138,6 +145,8 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE02_RUST_ERROR +#define CXXBRIDGE02_RUST_ERROR class Error final : std::exception { public: Error(const Error &); @@ -149,6 +158,7 @@ public: private: Str::Repr msg; }; +#endif // CXXBRIDGE02_RUST_ERROR std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -159,10 +169,13 @@ using str = Str; template using box = Box; using error = Error; +#ifndef CXXBRIDGE02_RUST_BITCOPY +#define CXXBRIDGE02_RUST_BITCOPY struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; }; constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE02_RUST_BITCOPY } // namespace cxxbridge02 } // namespace rust From 3577d451d7877ee00e765c2f93fd7e563ee45499 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 04:48:13 +0000 Subject: [PATCH 160/2232] Fix blank first line if no headers emitted --- diff --git a/gen/include.rs b/gen/include.rs index a7657e4..b19a097 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -26,7 +26,7 @@ fn find_line(line: &str) -> Option { } } -#[derive(Default)] +#[derive(Default, PartialEq)] pub struct Includes { custom: Vec, pub array: bool, @@ -74,6 +74,9 @@ impl Display for Includes { if self.type_traits { writeln!(f, "#include ")?; } + if *self != Self::default() { + writeln!(f)?; + } Ok(()) } } diff --git a/gen/out.rs b/gen/out.rs index 124816f..2a5b17d 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -53,14 +53,18 @@ impl Write for OutFile { fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { if !self.blocks_pending.is_empty() { - self.content.push(b'\n'); + if !self.content.is_empty() { + self.content.push(b'\n'); + } for block in self.blocks_pending.drain(..) { self.content.extend_from_slice(block.as_bytes()); self.content.extend_from_slice(b" {\n"); } self.section_pending = false; } else if self.section_pending { - self.content.push(b'\n'); + if !self.content.is_empty() { + self.content.push(b'\n'); + } self.section_pending = false; } self.content.extend_from_slice(s.as_bytes()); From 4791f1c115181788ebfea5572f64bff5549d801c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 04:55:19 +0000 Subject: [PATCH 161/2232] Include when using std::move --- diff --git a/gen/include.rs b/gen/include.rs index b19a097..a642c0a 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -36,6 +36,7 @@ pub struct Includes { pub memory: bool, pub string: bool, pub type_traits: bool, + pub utility: bool, } impl Includes { @@ -74,6 +75,9 @@ impl Display for Includes { if self.type_traits { writeln!(f, "#include ")?; } + if self.utility { + writeln!(f, "#include ")?; + } if *self != Self::default() { writeln!(f)?; } diff --git a/gen/write.rs b/gen/write.rs index a75d4c0..16322c1 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -178,6 +178,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_manually_drop { out.next_section(); + out.include.utility = true; writeln!(out, "template "); writeln!(out, "union ManuallyDrop {{"); writeln!(out, " T value;"); @@ -329,6 +330,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { arg.ident, ); } else if types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); } else { write!(out, "{}", arg.ident); @@ -347,6 +349,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ";"); if efn.throws { out.include.cstring = true; + out.include.exception = true; writeln!(out, " throw$.ptr = nullptr;"); writeln!(out, " }} catch (const ::std::exception &catch$) {{"); writeln!(out, " const char *return$ = catch$.what();"); @@ -410,6 +413,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, " {{"); for arg in &efn.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); write_type(out, &arg.ty); writeln!(out, "> {}$(::std::move({0}));", arg.ident); @@ -481,6 +485,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, " }}"); } if indirect_return { + out.include.utility = true; writeln!(out, " return ::std::move(return$.value);"); } writeln!(out, "}}"); @@ -668,6 +673,8 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { } fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { + out.include.utility = true; + let mut inner = String::new(); for name in &out.namespace { inner += name; diff --git a/include/cxx.h b/include/cxx.h index a021ca7..1026743 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace rust { inline namespace cxxbridge02 { From 5d12144989e81ae8a2a9e4e305a311cfcc98abfd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 05:22:24 +0000 Subject: [PATCH 162/2232] Factor catch implementation to static function --- diff --git a/gen/write.rs b/gen/write.rs index 16322c1..33674b4 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -125,9 +125,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_unsafe_bitcopy = false; let mut needs_manually_drop = false; let mut needs_maybe_uninit = false; + let mut needs_trycatch = false; for api in apis { match api { Api::CxxFunction(efn) if !out.header => { + if efn.throws { + needs_trycatch = true; + } for arg in &efn.args { if arg.ty == RustString { needs_unsafe_bitcopy = true; @@ -166,6 +170,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit + || needs_trycatch { writeln!(out, "// #include \"rust/cxx.h\""); } @@ -200,6 +205,20 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } + if needs_trycatch { + out.next_section(); + out.include.exception = true; + writeln!(out, "template "); + writeln!( + out, + "static void trycatch(Try &&func, Fail &&fail) noexcept try {{", + ); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + } + out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -297,8 +316,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " try {{"); - write!(out, " "); + writeln!(out, " ::rust::trycatch("); + writeln!(out, " [&] {{"); + write!(out, " "); } if indirect_return { write!(out, "new (return$) "); @@ -349,16 +369,15 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ";"); if efn.throws { out.include.cstring = true; - out.include.exception = true; - writeln!(out, " throw$.ptr = nullptr;"); - writeln!(out, " }} catch (const ::std::exception &catch$) {{"); - writeln!(out, " const char *return$ = catch$.what();"); - writeln!(out, " throw$.len = ::std::strlen(return$);"); + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }},"); + writeln!(out, " [&](const char *catch$) {{"); + writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge02$exception(return$, throw$.len);", + " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", ); - writeln!(out, " }}"); + writeln!(out, " }});"); writeln!(out, " return throw$;"); } writeln!(out, "}}"); From b7a42acc73ef38ba011e643d94b1b345e0972c15 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 05:47:24 +0000 Subject: [PATCH 163/2232] Merge pull request #76 from dtolnay/trycatch Factor catch implementation to static function --- diff --git a/gen/write.rs b/gen/write.rs index 16322c1..33674b4 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -125,9 +125,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_unsafe_bitcopy = false; let mut needs_manually_drop = false; let mut needs_maybe_uninit = false; + let mut needs_trycatch = false; for api in apis { match api { Api::CxxFunction(efn) if !out.header => { + if efn.throws { + needs_trycatch = true; + } for arg in &efn.args { if arg.ty == RustString { needs_unsafe_bitcopy = true; @@ -166,6 +170,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit + || needs_trycatch { writeln!(out, "// #include \"rust/cxx.h\""); } @@ -200,6 +205,20 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } + if needs_trycatch { + out.next_section(); + out.include.exception = true; + writeln!(out, "template "); + writeln!( + out, + "static void trycatch(Try &&func, Fail &&fail) noexcept try {{", + ); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + } + out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -297,8 +316,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " try {{"); - write!(out, " "); + writeln!(out, " ::rust::trycatch("); + writeln!(out, " [&] {{"); + write!(out, " "); } if indirect_return { write!(out, "new (return$) "); @@ -349,16 +369,15 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ";"); if efn.throws { out.include.cstring = true; - out.include.exception = true; - writeln!(out, " throw$.ptr = nullptr;"); - writeln!(out, " }} catch (const ::std::exception &catch$) {{"); - writeln!(out, " const char *return$ = catch$.what();"); - writeln!(out, " throw$.len = ::std::strlen(return$);"); + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }},"); + writeln!(out, " [&](const char *catch$) {{"); + writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge02$exception(return$, throw$.len);", + " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", ); - writeln!(out, " }}"); + writeln!(out, " }});"); writeln!(out, " return throw$;"); } writeln!(out, "}}"); From 82c1617925687ec3d5eace66150c40f1eb555502 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 05:54:12 +0000 Subject: [PATCH 164/2232] Catch block isn't allowed to throw --- diff --git a/gen/write.rs b/gen/write.rs index 33674b4..99260ea 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -371,7 +371,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { out.include.cstring = true; writeln!(out, " throw$.ptr = nullptr;"); writeln!(out, " }},"); - writeln!(out, " [&](const char *catch$) {{"); + writeln!(out, " [&](const char *catch$) noexcept {{"); writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, From 3e3e0af976aab5ec8095c464093ebb6665298db7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 06:03:24 +0000 Subject: [PATCH 165/2232] Allow catch behavior to be customized If the user's cxx::bridge invocation includes any header that defines the following function, they get it's behavior as the exception-to-Result conversion. namespace rust::behavior { template static void trycatch(Try &&func, Fail &&fail) noexcept try { func(); } catch (/* up to you */) { fail(/* const char *msg */); } } The default behavior is equivalent to: } catch (const std::exception &e) { fail(e.what()); } Codebases that use Folly, for example, may be interested in behavior like this instead for better type information on the error messages: } catch (const std::exception &e) { fail(folly::exceptionStr(e)); } catch (...) { fail(""); } --- diff --git a/gen/write.rs b/gen/write.rs index 99260ea..de2d0cd 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -205,10 +205,21 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } + out.end_block("namespace cxxbridge02"); + if needs_trycatch { - out.next_section(); + out.begin_block("namespace behavior"); out.include.exception = true; - writeln!(out, "template "); + writeln!(out, "struct trycatch {{"); + writeln!(out, " template trycatch(T);"); + writeln!(out, " static char use_default;"); + writeln!(out, "}};"); + writeln!(out); + writeln!(out, "template ()).use_default)>", + ); writeln!( out, "static void trycatch(Try &&func, Fail &&fail) noexcept try {{", @@ -217,9 +228,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}} catch (const ::std::exception &e) {{"); writeln!(out, " fail(e.what());"); writeln!(out, "}}"); + out.end_block("namespace behavior"); } - out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -316,7 +327,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " ::rust::trycatch("); + writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); write!(out, " "); } From cf2de24613d6d7e31941df4508701e935835edad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 06:20:57 +0000 Subject: [PATCH 166/2232] Add Result to builtin types table Closes #53. --- diff --git a/README.md b/README.md index ea82afb..7909711 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ of functions. CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +Result<T>error ↔️ exceptionallowed as return type only The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in @@ -336,7 +337,6 @@ the facets that I still intend for this project to tackle: - [ ] Support associated methods: `extern "Rust" { fn f(self: &Struct); }` - [ ] Support C++ member functions - [ ] Support passing function pointers across the FFI -- [ ] Support translating between Result ⟷ exceptions - [ ] Support structs with type parameters - [ ] Support async functions diff --git a/src/lib.rs b/src/lib.rs index 5dd2649..b03c234 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -306,6 +306,7 @@ //! CxxStringstd::stringcannot be passed by value //! Box<T>rust::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +//! Result<T>error ↔️ exceptionallowed as return type only //! //! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file From 06515f0ed2d59cd6c8451e9fbf3e951c8b4bad72 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 06:28:02 +0000 Subject: [PATCH 167/2232] Replace arrows with html entity --- diff --git a/README.md b/README.md index 7909711..fe54024 100644 --- a/README.md +++ b/README.md @@ -291,9 +291,9 @@ Some of the considerations that go into ensuring safety are: ## Builtin types -In addition to all the primitive types (i32 ⟷ int32_t), the following common -types may be used in the fields of shared structs and the arguments and returns -of functions. +In addition to all the primitive types (i32 ⟷ int32_t), the following +common types may be used in the fields of shared structs and the arguments and +returns of functions. @@ -302,7 +302,7 @@ of functions. - +
name in Rustname in C++restrictions
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Result<T>error ↔️ exceptionallowed as return type only
Result<T>error ↔ exceptionallowed as return type only
The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in diff --git a/src/lib.rs b/src/lib.rs index b03c234..cc9fb12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,9 +295,9 @@ //! //! # Builtin types //! -//! In addition to all the primitive types (i32 ⟷ int32_t), the following common -//! types may be used in the fields of shared structs and the arguments and -//! returns of functions. +//! In addition to all the primitive types (i32 ⟷ int32_t), the following +//! common types may be used in the fields of shared structs and the arguments +//! and returns of functions. //! //! //! @@ -306,7 +306,7 @@ //! //! //! -//! +//! //!
name in Rustname in C++restrictions
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Result<T>error ↔️ exceptionallowed as return type only
Result<T>error ↔ exceptionallowed as return type only
//! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file From 559fbb3918a8276383ed00eb64ed4199441d8261 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 06:32:22 +0000 Subject: [PATCH 168/2232] Give up and use ascii arrows None of the unicode ones look consistently good across different systems. --- diff --git a/README.md b/README.md index fe54024..4308291 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ Some of the considerations that go into ensuring safety are: ## Builtin types -In addition to all the primitive types (i32 ⟷ int32_t), the following +In addition to all the primitive types (i32 <=> int32_t), the following common types may be used in the fields of shared structs and the arguments and returns of functions. @@ -302,7 +302,7 @@ returns of functions. CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type -Result<T>error ↔ exceptionallowed as return type only +Result<T>error <=> exceptionallowed as return type only The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in diff --git a/src/lib.rs b/src/lib.rs index cc9fb12..8d1ba19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,9 +295,9 @@ //! //! # Builtin types //! -//! In addition to all the primitive types (i32 ⟷ int32_t), the following -//! common types may be used in the fields of shared structs and the arguments -//! and returns of functions. +//! In addition to all the primitive types (i32 <=> int32_t), the +//! following common types may be used in the fields of shared structs and the +//! arguments and returns of functions. //! //! //! @@ -306,7 +306,7 @@ //! //! //! -//! +//! //!
name in Rustname in C++restrictions
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Result<T>error ↔ exceptionallowed as return type only
Result<T>error <=> exceptionallowed as return type only
//! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file From 047223384ee12c63da523d61a7bdbb4d740e2e1b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 18:35:39 +0000 Subject: [PATCH 169/2232] Make trycatch sfinae work on msvc --- diff --git a/gen/write.rs b/gen/write.rs index de2d0cd..9a33fc5 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -210,20 +210,19 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_trycatch { out.begin_block("namespace behavior"); out.include.exception = true; - writeln!(out, "struct trycatch {{"); - writeln!(out, " template trycatch(T);"); - writeln!(out, " static char use_default;"); - writeln!(out, "}};"); + out.include.type_traits = true; + out.include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); writeln!(out); - writeln!(out, "template ()).use_default)>", - ); + writeln!(out, "template "); + writeln!(out, "static typename std::enable_if<"); writeln!( out, - "static void trycatch(Try &&func, Fail &&fail) noexcept try {{", + " std::is_same(), std::declval())),", ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); writeln!(out, " func();"); writeln!(out, "}} catch (const ::std::exception &e) {{"); writeln!(out, " fail(e.what());"); From 57d01e41071b12f10c9598132124833981d0481c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 18:53:57 +0000 Subject: [PATCH 170/2232] Merge pull request #77 from dtolnay/catch Allow catch behavior to be customized --- diff --git a/gen/write.rs b/gen/write.rs index 99260ea..9a33fc5 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -205,21 +205,31 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } + out.end_block("namespace cxxbridge02"); + if needs_trycatch { - out.next_section(); + out.begin_block("namespace behavior"); out.include.exception = true; + out.include.type_traits = true; + out.include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); + writeln!(out); writeln!(out, "template "); + writeln!(out, "static typename std::enable_if<"); writeln!( out, - "static void trycatch(Try &&func, Fail &&fail) noexcept try {{", + " std::is_same(), std::declval())),", ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); writeln!(out, " func();"); writeln!(out, "}} catch (const ::std::exception &e) {{"); writeln!(out, " fail(e.what());"); writeln!(out, "}}"); + out.end_block("namespace behavior"); } - out.end_block("namespace cxxbridge02"); out.end_block("namespace rust"); } @@ -316,7 +326,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " ::rust::trycatch("); + writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); write!(out, " "); } From e68634c0a1624dc57c69ff277892e9addf14bad8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 19:03:40 +0000 Subject: [PATCH 171/2232] Fix missing newline after cxxbridge02$exception declaration --- diff --git a/gen/write.rs b/gen/write.rs index 9a33fc5..ff5670c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -278,7 +278,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { if has_cxx_throws { out.next_section(); - write!( + writeln!( out, "const char *cxxbridge02$exception(const char *, size_t);", ); From 16448731f2923bfa02f1bc015c51c9a98ac43659 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 19:40:26 +0000 Subject: [PATCH 172/2232] Move extern fn signature fields to struct This will allow reusing the Signature struct for the type of function pointers. --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 741431a..dfaab55 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,6 +1,15 @@ -use crate::syntax::{Ref, Ty1, Type}; +use crate::syntax::{ExternFn, Ref, Signature, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; +use std::ops::Deref; + +impl Deref for ExternFn { + type Target = Signature; + + fn deref(&self) -> &Self::Target { + &self.sig + } +} impl Hash for Type { fn hash(&self, state: &mut H) { diff --git a/syntax/mod.rs b/syntax/mod.rs index 79a4a7b..f2e1c1c 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -46,13 +46,17 @@ pub struct Struct { pub struct ExternFn { pub lang: Lang, pub doc: Doc, - pub fn_token: Token![fn], pub ident: Ident, + pub sig: Signature, + pub semi_token: Token![;], +} + +pub struct Signature { + pub fn_token: Token![fn], pub receiver: Option, pub args: Vec, pub ret: Option, pub throws: bool, - pub semi_token: Token![;], } pub struct Var { diff --git a/syntax/parse.rs b/syntax/parse.rs index 00ea15c..e07ee25 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,6 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Struct, Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Struct, + Ty1, Type, Var, }; use proc_macro2::Ident; use quote::quote; @@ -207,12 +208,14 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { Ok(ExternFn { lang, doc, - fn_token, ident, - receiver, - args, - ret, - throws, + sig: Signature { + fn_token, + receiver, + args, + ret, + throws, + }, semi_token, }) } From 0b76aea8e8ac0c31ec9b28deeb0cf4f68889a451 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 19:54:24 +0000 Subject: [PATCH 173/2232] Allow extern abi to be specified as "C++" --- diff --git a/syntax/parse.rs b/syntax/parse.rs index e07ee25..2258dd6 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -111,7 +111,7 @@ fn parse_lang(abi: Abi) -> Result { } }; match name.value().as_str() { - "C" => Ok(Lang::Cxx), + "C" | "C++" => Ok(Lang::Cxx), "Rust" => Ok(Lang::Rust), _ => Err(Error::new_spanned(abi, "unrecognized ABI")), } From b40b9dba076f33859faf383d80416355d2c65ac6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 20:50:36 +0000 Subject: [PATCH 174/2232] Divide up big parse_type function --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 2258dd6..10db16e 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,7 +6,8 @@ use proc_macro2::Ident; use quote::quote; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, + ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, TypePath, + TypeReference, }; pub fn parse_items(items: Vec) -> Result> { @@ -222,62 +223,64 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { fn parse_type(ty: &RustType) -> Result { match ty { - RustType::Reference(ty) => { - let inner = parse_type(&ty.elem)?; - let which = match &inner { - Type::Ident(ident) if ident == "str" => { - if ty.mutability.is_some() { - return Err(Error::new_spanned(ty, "unsupported type")); - } else { - Type::Str - } - } - _ => Type::Ref, - }; - return Ok(which(Box::new(Ref { - ampersand: ty.and_token, - mutability: ty.mutability, - inner, - }))); + RustType::Reference(ty) => parse_type_reference(ty), + RustType::Path(ty) => parse_type_path(ty), + RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), + _ => Err(Error::new_spanned(ty, "unsupported type")), + } +} + +fn parse_type_reference(ty: &TypeReference) -> Result { + let inner = parse_type(&ty.elem)?; + let which = match &inner { + Type::Ident(ident) if ident == "str" => { + if ty.mutability.is_some() { + return Err(Error::new_spanned(ty, "unsupported type")); + } else { + Type::Str + } } - RustType::Path(ty) => { - let path = &ty.path; - if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { - let segment = &path.segments[0]; - let ident = segment.ident.clone(); - match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(ident)), - PathArguments::AngleBracketed(generic) => { - if ident == "UniquePtr" && generic.args.len() == 1 { - if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; - return Ok(Type::UniquePtr(Box::new(Ty1 { - name: ident, - langle: generic.lt_token, - inner, - rangle: generic.gt_token, - }))); - } - } else if ident == "Box" && generic.args.len() == 1 { - if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; - return Ok(Type::RustBox(Box::new(Ty1 { - name: ident, - langle: generic.lt_token, - inner, - rangle: generic.gt_token, - }))); - } - } + _ => Type::Ref, + }; + Ok(which(Box::new(Ref { + ampersand: ty.and_token, + mutability: ty.mutability, + inner, + }))) +} + +fn parse_type_path(ty: &TypePath) -> Result { + let path = &ty.path; + if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { + let segment = &path.segments[0]; + let ident = segment.ident.clone(); + match &segment.arguments { + PathArguments::None => return Ok(Type::Ident(ident)), + PathArguments::AngleBracketed(generic) => { + if ident == "UniquePtr" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::UniquePtr(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } + } else if ident == "Box" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::RustBox(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); } - PathArguments::Parenthesized(_) => {} } } + PathArguments::Parenthesized(_) => {} } - RustType::Tuple(ty) if ty.elems.is_empty() => { - return Ok(Type::Void(ty.paren_token.span)); - } - _ => {} } Err(Error::new_spanned(ty, "unsupported type")) } From 6dfa3b0a7c9471bf23fd51c5e21b25b77d2b81e1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 21:01:12 +0000 Subject: [PATCH 175/2232] Update third-party dependencies --- diff --git a/third-party/BUCK b/third-party/BUCK index 7dd18fe..ccb2107 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -2,7 +2,7 @@ rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + srcs = glob(["vendor/anyhow-1.0.27/src/**"]), visibility = ["PUBLIC"], features = ["std"], ) @@ -115,7 +115,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.11/src/**"]), + srcs = glob(["vendor/structopt-0.3.12/src/**"]), visibility = ["PUBLIC"], deps = [ ":clap", @@ -126,7 +126,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.4/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.5/src/**"]), proc_macro = True, deps = [ ":heck", diff --git a/third-party/BUILD b/third-party/BUILD index 93e5426..ad779fd 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -7,7 +7,7 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.26/src/**"]), + srcs = glob(["vendor/anyhow-1.0.27/src/**"]), crate_features = ["std"], visibility = ["//visibility:public"], ) @@ -120,7 +120,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.11/src/**"]), + srcs = glob(["vendor/structopt-0.3.12/src/**"]), visibility = ["//visibility:public"], deps = [ ":clap", @@ -131,7 +131,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.4/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.5/src/**"]), crate_type = "proc-macro", deps = [ ":heck", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b0ac2a8..d8c5cb0 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" [[package]] name = "atty" @@ -168,9 +168,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.67" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "link-cplusplus" @@ -238,24 +238,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" dependencies = [ "proc-macro2", "quote", @@ -281,9 +281,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073" +checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d" dependencies = [ "clap", "lazy_static", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" +checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" dependencies = [ "heck", "proc-macro-error", From e2e7bc3c9ed932dfd08a589ab782f591fd8d80b7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 22:17:37 +0000 Subject: [PATCH 176/2232] Update to codespan-reporting 0.9 --- diff --git a/BUCK b/BUCK index 2f80c9b..4357dd6 100644 --- a/BUCK +++ b/BUCK @@ -7,7 +7,6 @@ rust_library( ":macro", "//third-party:anyhow", "//third-party:cc", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:link-cplusplus", "//third-party:proc-macro2", @@ -26,7 +25,6 @@ rust_binary( }, deps = [ "//third-party:anyhow", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", diff --git a/BUILD b/BUILD index db22203..59ac9fc 100644 --- a/BUILD +++ b/BUILD @@ -10,7 +10,6 @@ rust_library( ":cxxbridge-macro", "//third-party:anyhow", "//third-party:cc", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:link-cplusplus", "//third-party:proc-macro2", @@ -27,7 +26,6 @@ rust_binary( visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", diff --git a/Cargo.toml b/Cargo.toml index 8432bc7..63a3a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" cc = "1.0.49" -codespan = "0.7" -codespan-reporting = "0.7" +codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.0", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 7ee974c..aef31f6 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -16,8 +16,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" -codespan = "0.7" -codespan-reporting = "0.7" +codespan-reporting = "0.9" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" structopt = "0.3" diff --git a/gen/error.rs b/gen/error.rs index b8ac337..ca78acb 100644 --- a/gen/error.rs +++ b/gen/error.rs @@ -1,8 +1,8 @@ use crate::gen::Error; use crate::syntax; use anyhow::anyhow; -use codespan::{FileId, Files}; use codespan_reporting::diagnostic::{Diagnostic, Label}; +use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; use codespan_reporting::term::{self, Config}; use std::io::Write; @@ -46,32 +46,30 @@ fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, err } end_offset += end.column; - let mut files = Files::new(); + let mut files = SimpleFiles::new(); let file = files.add(path.to_string_lossy(), source); - let range = start_offset as u32..end_offset as u32; - let diagnostic = diagnose(file, range, error); + let diagnostic = diagnose(file, start_offset..end_offset, error); let config = Config::default(); let _ = term::emit(stderr, &config, &files, &diagnostic); } -fn diagnose(file: FileId, range: Range, error: syn::Error) -> Diagnostic { +fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { let message = error.to_string(); let info = syntax::error::ERRORS .iter() .find(|e| message.contains(e.msg)); - let mut diagnostic = if let Some(info) = info { - let label = Label::new(file, range, info.label.unwrap_or(&message)); - let mut diagnostic = Diagnostic::new_error(&message, label); - if let Some(note) = info.note { - diagnostic = diagnostic.with_notes(vec![note.to_owned()]); - } - diagnostic + let mut diagnostic = Diagnostic::error().with_message(&message); + let mut label = Label::primary(file, range); + if let Some(info) = info { + label.message = info.label.map_or(message, str::to_owned); + diagnostic.labels.push(label); + diagnostic.notes.extend(info.note.map(str::to_owned)); } else { - let label = Label::new(file, range, &message); - Diagnostic::new_error(&message, label) - }; + label.message = message; + diagnostic.labels.push(label); + } diagnostic.code = Some("cxxbridge".to_owned()); diagnostic } diff --git a/third-party/BUCK b/third-party/BUCK index ccb2107..14f2542 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -30,18 +30,10 @@ rust_library( ) rust_library( - name = "codespan", - srcs = glob(["vendor/codespan-0.7.0/src/**"]), - visibility = ["PUBLIC"], - deps = [":unicode-segmentation"], -) - -rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), visibility = ["PUBLIC"], deps = [ - ":codespan", ":termcolor", ":unicode-width", ], diff --git a/third-party/BUILD b/third-party/BUILD index ad779fd..daf9ea1 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -35,18 +35,10 @@ rust_library( ) rust_library( - name = "codespan", - srcs = glob(["vendor/codespan-0.7.0/src/**"]), - visibility = ["//visibility:public"], - deps = [":unicode-segmentation"], -) - -rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), visibility = ["//visibility:public"], deps = [ - ":codespan", ":termcolor", ":unicode-width", ], diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index d8c5cb0..e76c831 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -54,21 +54,11 @@ dependencies = [ ] [[package]] -name = "codespan" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" -dependencies = [ - "unicode-segmentation", -] - -[[package]] name = "codespan-reporting" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "657b2c99e1f17bc3e5153808d9f704c8ba6171c3fe45e69fde26e2876156938b" +checksum = "7606d610349258b637bb639f7565bb7ee5fd6114130d2af59d0f39154e92426a" dependencies = [ - "codespan", "termcolor", "unicode-width", ] @@ -79,7 +69,6 @@ version = "0.2.0" dependencies = [ "anyhow", "cc", - "codespan", "codespan-reporting", "cxx-test-suite", "cxxbridge-macro", @@ -104,7 +93,6 @@ name = "cxxbridge-cmd" version = "0.2.0" dependencies = [ "anyhow", - "codespan", "codespan-reporting", "proc-macro2", "quote", From f1d3a38e3f0c18c2acf2d579acc3f58fc45908da Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 22:35:11 +0000 Subject: [PATCH 177/2232] Merge pull request #78 from dtolnay/codespan Update to codespan-reporting 0.9 --- diff --git a/BUCK b/BUCK index 2f80c9b..4357dd6 100644 --- a/BUCK +++ b/BUCK @@ -7,7 +7,6 @@ rust_library( ":macro", "//third-party:anyhow", "//third-party:cc", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:link-cplusplus", "//third-party:proc-macro2", @@ -26,7 +25,6 @@ rust_binary( }, deps = [ "//third-party:anyhow", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", diff --git a/BUILD b/BUILD index db22203..59ac9fc 100644 --- a/BUILD +++ b/BUILD @@ -10,7 +10,6 @@ rust_library( ":cxxbridge-macro", "//third-party:anyhow", "//third-party:cc", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:link-cplusplus", "//third-party:proc-macro2", @@ -27,7 +26,6 @@ rust_binary( visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", - "//third-party:codespan", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", diff --git a/Cargo.toml b/Cargo.toml index 8432bc7..63a3a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" cc = "1.0.49" -codespan = "0.7" -codespan-reporting = "0.7" +codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.0", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 7ee974c..aef31f6 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -16,8 +16,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" -codespan = "0.7" -codespan-reporting = "0.7" +codespan-reporting = "0.9" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" structopt = "0.3" diff --git a/gen/error.rs b/gen/error.rs index b8ac337..ca78acb 100644 --- a/gen/error.rs +++ b/gen/error.rs @@ -1,8 +1,8 @@ use crate::gen::Error; use crate::syntax; use anyhow::anyhow; -use codespan::{FileId, Files}; use codespan_reporting::diagnostic::{Diagnostic, Label}; +use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; use codespan_reporting::term::{self, Config}; use std::io::Write; @@ -46,32 +46,30 @@ fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, err } end_offset += end.column; - let mut files = Files::new(); + let mut files = SimpleFiles::new(); let file = files.add(path.to_string_lossy(), source); - let range = start_offset as u32..end_offset as u32; - let diagnostic = diagnose(file, range, error); + let diagnostic = diagnose(file, start_offset..end_offset, error); let config = Config::default(); let _ = term::emit(stderr, &config, &files, &diagnostic); } -fn diagnose(file: FileId, range: Range, error: syn::Error) -> Diagnostic { +fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { let message = error.to_string(); let info = syntax::error::ERRORS .iter() .find(|e| message.contains(e.msg)); - let mut diagnostic = if let Some(info) = info { - let label = Label::new(file, range, info.label.unwrap_or(&message)); - let mut diagnostic = Diagnostic::new_error(&message, label); - if let Some(note) = info.note { - diagnostic = diagnostic.with_notes(vec![note.to_owned()]); - } - diagnostic + let mut diagnostic = Diagnostic::error().with_message(&message); + let mut label = Label::primary(file, range); + if let Some(info) = info { + label.message = info.label.map_or(message, str::to_owned); + diagnostic.labels.push(label); + diagnostic.notes.extend(info.note.map(str::to_owned)); } else { - let label = Label::new(file, range, &message); - Diagnostic::new_error(&message, label) - }; + label.message = message; + diagnostic.labels.push(label); + } diagnostic.code = Some("cxxbridge".to_owned()); diagnostic } diff --git a/third-party/BUCK b/third-party/BUCK index ccb2107..14f2542 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -30,18 +30,10 @@ rust_library( ) rust_library( - name = "codespan", - srcs = glob(["vendor/codespan-0.7.0/src/**"]), - visibility = ["PUBLIC"], - deps = [":unicode-segmentation"], -) - -rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), visibility = ["PUBLIC"], deps = [ - ":codespan", ":termcolor", ":unicode-width", ], diff --git a/third-party/BUILD b/third-party/BUILD index ad779fd..daf9ea1 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -35,18 +35,10 @@ rust_library( ) rust_library( - name = "codespan", - srcs = glob(["vendor/codespan-0.7.0/src/**"]), - visibility = ["//visibility:public"], - deps = [":unicode-segmentation"], -) - -rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.7.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), visibility = ["//visibility:public"], deps = [ - ":codespan", ":termcolor", ":unicode-width", ], diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index d8c5cb0..e76c831 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -54,21 +54,11 @@ dependencies = [ ] [[package]] -name = "codespan" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" -dependencies = [ - "unicode-segmentation", -] - -[[package]] name = "codespan-reporting" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "657b2c99e1f17bc3e5153808d9f704c8ba6171c3fe45e69fde26e2876156938b" +checksum = "7606d610349258b637bb639f7565bb7ee5fd6114130d2af59d0f39154e92426a" dependencies = [ - "codespan", "termcolor", "unicode-width", ] @@ -79,7 +69,6 @@ version = "0.2.0" dependencies = [ "anyhow", "cc", - "codespan", "codespan-reporting", "cxx-test-suite", "cxxbridge-macro", @@ -104,7 +93,6 @@ name = "cxxbridge-cmd" version = "0.2.0" dependencies = [ "anyhow", - "codespan", "codespan-reporting", "proc-macro2", "quote", From 35c82eb673314dee3b4e77cc600d1d2fa454c6c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 18 2020 23:48:36 +0000 Subject: [PATCH 178/2232] Prevent forgetting fields in PartialEq and Hash impls --- diff --git a/syntax/impls.rs b/syntax/impls.rs index dfaab55..6baa122 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -45,14 +45,32 @@ impl Eq for Ty1 {} impl PartialEq for Ty1 { fn eq(&self, other: &Ty1) -> bool { - self.name == other.name && self.inner == other.inner + 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(&self, state: &mut H) { - self.name.hash(state); - self.inner.hash(state); + let Ty1 { + name, + langle: _, + inner, + rangle: _, + } = self; + name.hash(state); + inner.hash(state); } } @@ -60,12 +78,28 @@ impl Eq for Ref {} impl PartialEq for Ref { fn eq(&self, other: &Ref) -> bool { - self.inner == other.inner + let Ref { + ampersand: _, + mutability, + inner, + } = self; + let Ref { + ampersand: _, + mutability: mutability2, + inner: inner2, + } = other; + mutability.is_some() == mutability2.is_some() && inner == inner2 } } impl Hash for Ref { fn hash(&self, state: &mut H) { - self.inner.hash(state); + let Ref { + ampersand: _, + mutability, + inner, + } = self; + mutability.is_some().hash(state); + inner.hash(state); } } From 417305a41aa8145dab3a7edf24c5a4e65fbbaeb0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 00:03:26 +0000 Subject: [PATCH 179/2232] Add function pointer types to syntax tree --- diff --git a/gen/write.rs b/gen/write.rs index ff5670c..8883d9b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -608,6 +608,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } + Type::Fn(_) => unimplemented!(), Type::Void(_) => unreachable!(), } } @@ -617,6 +618,7 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), Type::Ref(_) => {} + Type::Fn(_) => unimplemented!(), Type::Void(_) => unreachable!(), } } diff --git a/syntax/check.rs b/syntax/check.rs index ef3faff..be61226 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -167,6 +167,7 @@ fn describe(ty: &Type, types: &Types) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), } } diff --git a/syntax/impls.rs b/syntax/impls.rs index 6baa122..57b42d1 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,4 @@ -use crate::syntax::{ExternFn, Ref, Signature, Ty1, Type}; +use crate::syntax::{ExternFn, Receiver, Ref, Signature, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -20,6 +20,7 @@ impl Hash for Type { Type::UniquePtr(t) => t.hash(state), Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), + Type::Fn(t) => t.hash(state), Type::Void(_) => {} } } @@ -35,6 +36,7 @@ impl PartialEq for Type { (Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs, (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, + (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::Void(_), Type::Void(_)) => true, (_, _) => false, } @@ -103,3 +105,62 @@ impl Hash for Ref { inner.hash(state); } } + +impl Eq for Signature {} + +impl PartialEq for Signature { + fn eq(&self, other: &Signature) -> bool { + let Signature { + fn_token: _, + receiver, + args, + ret, + throws, + } = self; + let Signature { + fn_token: _, + receiver: receiver2, + args: args2, + ret: ret2, + throws: throws2, + } = other; + receiver == receiver2 && args == args2 && ret == ret2 && throws == throws2 + } +} + +impl Hash for Signature { + fn hash(&self, state: &mut H) { + let Signature { + fn_token: _, + receiver, + args, + ret, + throws, + } = self; + receiver.hash(state); + args.hash(state); + ret.hash(state); + throws.hash(state); + } +} + +impl Eq for Receiver {} + +impl PartialEq for Receiver { + fn eq(&self, other: &Receiver) -> bool { + let Receiver { mutability, ident } = self; + let Receiver { + mutability: mutability2, + ident: ident2, + } = other; + mutability.is_some() == mutability2.is_some() && ident == ident2 + } +} + +impl Hash for Receiver { + fn hash(&self, state: &mut H) { + let Receiver { mutability, ident } = self; + mutability.is_some().hash(state); + ident.hash(state); + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index f2e1c1c..6274cf1 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -59,6 +59,7 @@ pub struct Signature { pub throws: bool, } +#[derive(Eq, PartialEq, Hash)] pub struct Var { pub ident: Ident, pub ty: Type, @@ -75,6 +76,7 @@ pub enum Type { UniquePtr(Box), Ref(Box), Str(Box), + Fn(Box), Void(Span), } diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 52c47d5..3bbe872 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{Derive, ExternFn, Ref, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote_spanned, ToTokens}; +use quote::{quote, quote_spanned, ToTokens}; use syn::Token; impl ToTokens for Type { @@ -16,6 +16,21 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Fn(f) => { + let fn_token = f.fn_token; + let args = &f.args; + tokens.extend(quote!(#fn_token(#(#args),*))); + let mut ret = match &f.ret { + Some(ret) => quote!(#ret), + None => quote!(()), + }; + if f.throws { + ret = quote!(::std::result::Result<#ret, _>); + } + if f.ret.is_some() || f.throws { + tokens.extend(quote!(-> #ret)); + } + } Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } } diff --git a/syntax/types.rs b/syntax/types.rs index f9f87f3..f65b12f 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -26,6 +26,14 @@ impl<'a> Types<'a> { Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), + Type::Fn(f) => { + if let Some(ret) = &f.ret { + visit(all, ret); + } + for arg in &f.args { + visit(all, &arg.ty); + } + } } } From d2bb3da09b77f8831b9428a15eb96b3b96f626e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 00:19:39 +0000 Subject: [PATCH 180/2232] Suppress declare_interior_mutable_const lint --- diff --git a/src/lib.rs b/src/lib.rs index 8d1ba19..b3bf41c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -335,6 +335,7 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.1.2")] #![deny(improper_ctypes)] #![allow( + clippy::declare_interior_mutable_const, clippy::inherent_to_string, clippy::large_enum_variant, clippy::missing_safety_doc, From db96ed9f517562132622426548e94c775bcf20db Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 00:20:39 +0000 Subject: [PATCH 181/2232] Resolve transmute_ptr_to_ptr lint --- diff --git a/src/lib.rs b/src/lib.rs index b3bf41c..4dbc97a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,7 +345,6 @@ clippy::or_fun_call, clippy::ptr_arg, clippy::toplevel_ref_arg, - clippy::transmute_ptr_to_ptr, clippy::useless_let_if_seq )] diff --git a/src/rust_string.rs b/src/rust_string.rs index 258576d..113adfd 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -14,7 +14,7 @@ impl RustString { } pub fn from_ref(s: &String) -> &Self { - unsafe { std::mem::transmute::<&String, &RustString>(s) } + unsafe { &*(s as *const String as *const RustString) } } pub fn into_string(self) -> String { From 2d40845a49d46571056ba030b096529df54d3a6c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 00:24:16 +0000 Subject: [PATCH 182/2232] Eliminate use of transmute in Result::exception --- diff --git a/src/result.rs b/src/result.rs index 047260b..296efea 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,7 +1,6 @@ use crate::exception::Exception; use crate::rust_str::RustStr; use std::fmt::Display; -use std::mem; use std::ptr; use std::result::Result as StdResult; use std::slice; @@ -50,10 +49,10 @@ impl Result { Ok(()) } else { let err = self.err; - let slice = slice::from_raw_parts(err.ptr.as_ptr(), err.len); - let s = str::from_utf8_unchecked(slice); + let slice = slice::from_raw_parts_mut(err.ptr.as_ptr(), err.len); + let s = str::from_utf8_unchecked_mut(slice); Err(Exception { - what: mem::transmute::<*const str, Box>(s), + what: Box::from_raw(s), }) } } From 33d302978062f1384c5657f11d07401602edf481 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 01:18:36 +0000 Subject: [PATCH 183/2232] Add flag to inject additional #include lines --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index b2cde40..3577f72 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -33,6 +33,10 @@ struct Opt { /// Emit header with declarations only #[structopt(long)] header: bool, + + /// Any additional headers to #include + #[structopt(short, long)] + include: Vec, } fn write(content: impl AsRef<[u8]>) { @@ -42,9 +46,13 @@ fn write(content: impl AsRef<[u8]>) { fn main() { let opt = Opt::from_args(); + let gen = gen::Opt { + include: opt.include, + }; + match (opt.input, opt.header) { - (Some(input), true) => write(gen::do_generate_header(&input)), - (Some(input), false) => write(gen::do_generate_bridge(&input)), + (Some(input), true) => write(gen::do_generate_header(&input, gen)), + (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), (None, true) => write(include::HEADER), (None, false) => unreachable!(), // enforced by required_unless } diff --git a/gen/include.rs b/gen/include.rs index a642c0a..7bec492 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -49,6 +49,12 @@ impl Includes { } } +impl Extend for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter); + } +} + impl Display for Includes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for include in &self.custom { diff --git a/gen/mod.rs b/gen/mod.rs index df9f3ee..9a34705 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -36,17 +36,23 @@ struct Input { module: Vec, } -pub(super) fn do_generate_bridge(path: &Path) -> OutFile { +#[derive(Default)] +pub(super) struct Opt { + /// Any additional headers to #include + pub include: Vec, +} + +pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> OutFile { let header = false; - generate(path, header) + generate(path, opt, header) } -pub(super) fn do_generate_header(path: &Path) -> OutFile { +pub(super) fn do_generate_header(path: &Path, opt: Opt) -> OutFile { let header = true; - generate(path, header) + generate(path, opt, header) } -fn generate(path: &Path, header: bool) -> OutFile { +fn generate(path: &Path, opt: Opt, header: bool) -> OutFile { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), @@ -57,7 +63,7 @@ fn generate(path: &Path, header: bool) -> OutFile { let apis = syntax::parse_items(bridge.module)?; let types = Types::collect(&apis)?; check::typecheck(&apis, &types)?; - let out = write::gen(bridge.namespace, &apis, &types, header); + let out = write::gen(bridge.namespace, &apis, &types, opt, header); Ok(out) })() { Ok(out) => out, diff --git a/gen/write.rs b/gen/write.rs index 8883d9b..f0c9077 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,10 +1,16 @@ -use crate::gen::include; use crate::gen::out::OutFile; +use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var}; use proc_macro2::Ident; -pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: bool) -> OutFile { +pub(super) fn gen( + namespace: Vec, + apis: &[Api], + types: &Types, + opt: Opt, + header: bool, +) -> OutFile { let mut out_file = OutFile::new(namespace.clone(), header); let out = &mut out_file; @@ -12,6 +18,7 @@ pub(super) fn gen(namespace: Vec, apis: &[Api], types: &Types, header: b writeln!(out, "#pragma once"); } + out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { out.include.insert(include.value()); diff --git a/src/lib.rs b/src/lib.rs index 4dbc97a..09326f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -383,6 +383,7 @@ pub mod private { } use crate::error::Result; +use crate::gen::Opt; use anyhow::anyhow; use std::fs; use std::io::{self, Write}; @@ -465,13 +466,13 @@ impl Build { } fn try_generate_bridge(rust_source_file: &Path) -> Result { - let header = gen::do_generate_header(rust_source_file); + let header = gen::do_generate_header(rust_source_file, Opt::default()); let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; fs::write(&header_path, header)?; paths::symlink_header(&header_path, rust_source_file); - let bridge = gen::do_generate_bridge(rust_source_file); + let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; fs::write(&bridge_path, bridge)?; let mut build = paths::cc_build(); From d95b119931f3e167738110ab659b47add6451286 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 03:07:46 +0000 Subject: [PATCH 184/2232] Preserve original tokens of Signature --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 57b42d1..27aa43a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -116,6 +116,7 @@ impl PartialEq for Signature { args, ret, throws, + tokens: _, } = self; let Signature { fn_token: _, @@ -123,6 +124,7 @@ impl PartialEq for Signature { args: args2, ret: ret2, throws: throws2, + tokens: _, } = other; receiver == receiver2 && args == args2 && ret == ret2 && throws == throws2 } @@ -136,6 +138,7 @@ impl Hash for Signature { args, ret, throws, + tokens: _, } = self; receiver.hash(state); args.hash(state); diff --git a/syntax/mod.rs b/syntax/mod.rs index 6274cf1..7d2ca19 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -12,7 +12,7 @@ pub mod set; mod tokens; pub mod types; -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; use syn::{LitStr, Token}; pub use self::atom::Atom; @@ -57,6 +57,7 @@ pub struct Signature { pub args: Vec, pub ret: Option, pub throws: bool, + pub tokens: TokenStream, } #[derive(Eq, PartialEq, Hash)] diff --git a/syntax/parse.rs b/syntax/parse.rs index 10db16e..58237f3 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -205,7 +205,11 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); + let mut foreign_fn2 = foreign_fn.clone(); + foreign_fn2.attrs.clear(); + let tokens = quote!(#foreign_fn2); let semi_token = foreign_fn.semi_token; + Ok(ExternFn { lang, doc, @@ -216,6 +220,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { args, ret, throws, + tokens, }, semi_token, }) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 3bbe872..ed5b315 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -77,8 +77,6 @@ impl ToTokens for Derive { impl ToTokens for ExternFn { fn to_tokens(&self, tokens: &mut TokenStream) { - self.fn_token.to_tokens(tokens); - self.ident.to_tokens(tokens); - self.semi_token.to_tokens(tokens); + self.sig.tokens.to_tokens(tokens); } } From c071b89c2c7e618970609f068669c62b9ad55240 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 03:14:00 +0000 Subject: [PATCH 185/2232] Parse function pointer types --- diff --git a/syntax/check.rs b/syntax/check.rs index be61226..1fe3daf 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -45,6 +45,7 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(unsupported_reference_type(ty)); } } + Type::Fn(_) => errors.push(unimplemented_fn_type(ty)), _ => {} } } @@ -213,3 +214,7 @@ fn return_by_value(ty: &Type, types: &Types) -> Error { let message = format!("returning {} by value is not supported", desc); Error::new_spanned(ty, message) } + +fn unimplemented_fn_type(ty: &Type) -> Error { + Error::new_spanned(ty, "function pointer support is not implemented yet") +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 58237f3..3d35f78 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,11 +3,11 @@ use crate::syntax::{ Ty1, Type, Var, }; use proc_macro2::Ident; -use quote::quote; +use quote::{format_ident, quote}; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, TypePath, - TypeReference, + ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, + TypeBareFn, TypePath, TypeReference, }; pub fn parse_items(items: Vec) -> Result> { @@ -176,32 +176,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { } let mut throws = false; - let ret = match &foreign_fn.sig.output { - ReturnType::Default => None, - ReturnType::Type(_, ret) => { - let mut ret = ret.as_ref(); - if let RustType::Path(ty) = ret { - let path = &ty.path; - if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { - let segment = &path.segments[0]; - let ident = segment.ident.clone(); - if let PathArguments::AngleBracketed(generic) = &segment.arguments { - if ident == "Result" && generic.args.len() == 1 { - if let GenericArgument::Type(arg) = &generic.args[0] { - ret = arg; - throws = true; - } - } - } - } - } - match parse_type(ret)? { - Type::Void(_) => None, - ty => Some(ty), - } - } - }; - + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws)?; let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); @@ -230,6 +205,7 @@ fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), RustType::Path(ty) => parse_type_path(ty), + RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } @@ -290,6 +266,71 @@ fn parse_type_path(ty: &TypePath) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } +fn parse_type_fn(ty: &TypeBareFn) -> Result { + if ty.lifetimes.is_some() { + return Err(Error::new_spanned( + ty, + "function pointer with lifetime parameters is not supported yet", + )); + } + if ty.variadic.is_some() { + return Err(Error::new_spanned( + ty, + "variadic function pointer is not supported yet", + )); + } + let args = ty + .inputs + .iter() + .enumerate() + .map(|(i, arg)| { + let ty = parse_type(&arg.ty)?; + let ident = match &arg.name { + Some(ident) => ident.0.clone(), + None => format_ident!("_{}", i), + }; + Ok(Var { ident, ty }) + }) + .collect::>()?; + let mut throws = false; + let ret = parse_return_type(&ty.output, &mut throws)?; + let tokens = quote!(#ty); + Ok(Type::Fn(Box::new(Signature { + fn_token: ty.fn_token, + receiver: None, + args, + ret, + throws, + tokens, + }))) +} + +fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result> { + let mut ret = match ty { + ReturnType::Default => return Ok(None), + ReturnType::Type(_, ret) => ret.as_ref(), + }; + if let RustType::Path(ty) = ret { + let path = &ty.path; + if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { + let segment = &path.segments[0]; + let ident = segment.ident.clone(); + if let PathArguments::AngleBracketed(generic) = &segment.arguments { + if ident == "Result" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + ret = arg; + *throws = true; + } + } + } + } + } + match parse_type(ret)? { + Type::Void(_) => Ok(None), + ty => Ok(Some(ty)), + } +} + fn check_reserved_name(ident: &Ident) -> Result<()> { if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { Err(Error::new(ident.span(), "reserved name")) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index ed5b315..59bb0de 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{quote_spanned, ToTokens}; use syn::Token; impl ToTokens for Type { @@ -16,21 +16,7 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), - Type::Fn(f) => { - let fn_token = f.fn_token; - let args = &f.args; - tokens.extend(quote!(#fn_token(#(#args),*))); - let mut ret = match &f.ret { - Some(ret) => quote!(#ret), - None => quote!(()), - }; - if f.throws { - ret = quote!(::std::result::Result<#ret, _>); - } - if f.ret.is_some() || f.throws { - tokens.extend(quote!(-> #ret)); - } - } + Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } } @@ -80,3 +66,9 @@ impl ToTokens for ExternFn { self.sig.tokens.to_tokens(tokens); } } + +impl ToTokens for Signature { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.tokens.to_tokens(tokens); + } +} From 265f6a079dd32c772ce35420b12891e2141c18e2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 03:14:00 +0000 Subject: [PATCH 186/2232] Suppress some clippy lints in test suite --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fe6a364..bd9d4df 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::boxed_local, clippy::trivially_copy_pass_by_ref)] + use cxx::{CxxString, UniquePtr}; use std::fmt::{self, Display}; From a4dd952094b9d04af94dbc5839d3a46df3960ab0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 19 2020 03:31:52 +0000 Subject: [PATCH 187/2232] Merge pull request #79 from dtolnay/fn Parse function pointer types --- diff --git a/syntax/check.rs b/syntax/check.rs index be61226..1fe3daf 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -45,6 +45,7 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { errors.push(unsupported_reference_type(ty)); } } + Type::Fn(_) => errors.push(unimplemented_fn_type(ty)), _ => {} } } @@ -213,3 +214,7 @@ fn return_by_value(ty: &Type, types: &Types) -> Error { let message = format!("returning {} by value is not supported", desc); Error::new_spanned(ty, message) } + +fn unimplemented_fn_type(ty: &Type) -> Error { + Error::new_spanned(ty, "function pointer support is not implemented yet") +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 58237f3..3d35f78 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,11 +3,11 @@ use crate::syntax::{ Ty1, Type, Var, }; use proc_macro2::Ident; -use quote::quote; +use quote::{format_ident, quote}; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, TypePath, - TypeReference, + ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, + TypeBareFn, TypePath, TypeReference, }; pub fn parse_items(items: Vec) -> Result> { @@ -176,32 +176,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { } let mut throws = false; - let ret = match &foreign_fn.sig.output { - ReturnType::Default => None, - ReturnType::Type(_, ret) => { - let mut ret = ret.as_ref(); - if let RustType::Path(ty) = ret { - let path = &ty.path; - if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { - let segment = &path.segments[0]; - let ident = segment.ident.clone(); - if let PathArguments::AngleBracketed(generic) = &segment.arguments { - if ident == "Result" && generic.args.len() == 1 { - if let GenericArgument::Type(arg) = &generic.args[0] { - ret = arg; - throws = true; - } - } - } - } - } - match parse_type(ret)? { - Type::Void(_) => None, - ty => Some(ty), - } - } - }; - + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws)?; let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); @@ -230,6 +205,7 @@ fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), RustType::Path(ty) => parse_type_path(ty), + RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } @@ -290,6 +266,71 @@ fn parse_type_path(ty: &TypePath) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } +fn parse_type_fn(ty: &TypeBareFn) -> Result { + if ty.lifetimes.is_some() { + return Err(Error::new_spanned( + ty, + "function pointer with lifetime parameters is not supported yet", + )); + } + if ty.variadic.is_some() { + return Err(Error::new_spanned( + ty, + "variadic function pointer is not supported yet", + )); + } + let args = ty + .inputs + .iter() + .enumerate() + .map(|(i, arg)| { + let ty = parse_type(&arg.ty)?; + let ident = match &arg.name { + Some(ident) => ident.0.clone(), + None => format_ident!("_{}", i), + }; + Ok(Var { ident, ty }) + }) + .collect::>()?; + let mut throws = false; + let ret = parse_return_type(&ty.output, &mut throws)?; + let tokens = quote!(#ty); + Ok(Type::Fn(Box::new(Signature { + fn_token: ty.fn_token, + receiver: None, + args, + ret, + throws, + tokens, + }))) +} + +fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result> { + let mut ret = match ty { + ReturnType::Default => return Ok(None), + ReturnType::Type(_, ret) => ret.as_ref(), + }; + if let RustType::Path(ty) = ret { + let path = &ty.path; + if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { + let segment = &path.segments[0]; + let ident = segment.ident.clone(); + if let PathArguments::AngleBracketed(generic) = &segment.arguments { + if ident == "Result" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + ret = arg; + *throws = true; + } + } + } + } + } + match parse_type(ret)? { + Type::Void(_) => Ok(None), + ty => Ok(Some(ty)), + } +} + fn check_reserved_name(ident: &Ident) -> Result<()> { if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { Err(Error::new(ident.span(), "reserved name")) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index ed5b315..59bb0de 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{quote_spanned, ToTokens}; use syn::Token; impl ToTokens for Type { @@ -16,21 +16,7 @@ impl ToTokens for Type { } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), - Type::Fn(f) => { - let fn_token = f.fn_token; - let args = &f.args; - tokens.extend(quote!(#fn_token(#(#args),*))); - let mut ret = match &f.ret { - Some(ret) => quote!(#ret), - None => quote!(()), - }; - if f.throws { - ret = quote!(::std::result::Result<#ret, _>); - } - if f.ret.is_some() || f.throws { - tokens.extend(quote!(-> #ret)); - } - } + Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } } @@ -80,3 +66,9 @@ impl ToTokens for ExternFn { self.sig.tokens.to_tokens(tokens); } } + +impl ToTokens for Signature { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.tokens.to_tokens(tokens); + } +} From 30430f13c92a64f571f04b555b908da0b10e67e8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 20 2020 03:49:34 +0000 Subject: [PATCH 188/2232] Include for size_t --- diff --git a/gen/include.rs b/gen/include.rs index 7bec492..e3a9dd7 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -30,6 +30,7 @@ fn find_line(line: &str) -> Option { pub struct Includes { custom: Vec, pub array: bool, + pub cstddef: bool, pub cstdint: bool, pub cstring: bool, pub exception: bool, @@ -63,6 +64,9 @@ impl Display for Includes { if self.array { writeln!(f, "#include ")?; } + if self.cstddef { + writeln!(f, "#include ")?; + } if self.cstdint { writeln!(f, "#include ")?; } diff --git a/gen/write.rs b/gen/write.rs index f0c9077..a4e2eb2 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -91,10 +91,11 @@ fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => out.include.cstdint = true, + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) + | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, Some(CxxString) => out.include.string = true, - Some(Bool) | Some(F32) | Some(F64) | Some(RustString) | None => {} + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, diff --git a/include/cxx.h b/include/cxx.h index 1026743..50f8f69 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include From 09462acd4b1b89dd6e11af78973ae2a7ce270270 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 20 2020 21:58:56 +0000 Subject: [PATCH 189/2232] Disallow shared structs having 0 fields --- diff --git a/syntax/check.rs b/syntax/check.rs index 1fe3daf..742ecc2 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; -use proc_macro2::Ident; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var}; +use proc_macro2::{Delimiter, Group, Ident, TokenStream}; +use quote::quote; use syn::{Error, Result}; pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { @@ -53,6 +54,9 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { for api in apis { match api { Api::Struct(strct) => { + if strct.fields.is_empty() { + errors.push(struct_empty(strct)); + } for field in &strct.fields { if is_unsized(&field.ty, types) { errors.push(field_by_value(field, types)); @@ -197,6 +201,14 @@ fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error { Error::new_spanned(unique_ptr, "unsupported unique_ptr target type") } +fn struct_empty(strct: &Struct) -> Error { + let struct_token = strct.struct_token; + let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); + brace_token.set_span(strct.brace_token.span); + let span = quote!(#struct_token #brace_token); + Error::new_spanned(span, "structs without any fields are not supported") +} + fn field_by_value(field: &Var, types: &Types) -> Error { let desc = describe(&field.ty, types); let message = format!("using {} by value is not supported", desc); diff --git a/syntax/mod.rs b/syntax/mod.rs index 7d2ca19..38e21b0 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,7 +13,7 @@ mod tokens; pub mod types; use proc_macro2::{Ident, Span, TokenStream}; -use syn::{LitStr, Token}; +use syn::{token::Brace, LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -40,6 +40,7 @@ pub struct Struct { pub derives: Vec, pub struct_token: Token![struct], pub ident: Ident, + pub brace_token: Brace, pub fields: Vec, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 3d35f78..a00f8dd 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -46,26 +46,32 @@ fn parse_struct(item: ItemStruct) -> Result { let mut derives = Vec::new(); attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?; check_reserved_name(&item.ident)?; - match item.fields { - Fields::Named(fields) => Ok(Api::Struct(Struct { - doc, - derives, - struct_token: item.struct_token, - ident: item.ident, - fields: fields - .named - .into_iter() - .map(|field| { - Ok(Var { - ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, - }) + + let fields = match item.fields { + Fields::Named(fields) => fields, + Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")), + Fields::Unnamed(_) => { + return Err(Error::new_spanned(item, "tuple structs are not supported")) + } + }; + + Ok(Api::Struct(Struct { + doc, + derives, + struct_token: item.struct_token, + ident: item.ident, + brace_token: fields.brace_token, + fields: fields + .named + .into_iter() + .map(|field| { + Ok(Var { + ident: field.ident.unwrap(), + ty: parse_type(&field.ty)?, }) - .collect::>()?, - })), - Fields::Unit => Err(Error::new_spanned(item, "unit structs are not supported")), - Fields::Unnamed(_) => Err(Error::new_spanned(item, "tuple structs are not supported")), - } + }) + .collect::>()?, + })) } fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { diff --git a/tests/ui/empty_struct.rs b/tests/ui/empty_struct.rs new file mode 100644 index 0000000..060cfe0 --- /dev/null +++ b/tests/ui/empty_struct.rs @@ -0,0 +1,6 @@ +#[cxx::bridge] +mod ffi { + struct Empty {} +} + +fn main() {} diff --git a/tests/ui/empty_struct.stderr b/tests/ui/empty_struct.stderr new file mode 100644 index 0000000..612476b --- /dev/null +++ b/tests/ui/empty_struct.stderr @@ -0,0 +1,5 @@ +error: structs without any fields are not supported + --> $DIR/empty_struct.rs:3:5 + | +3 | struct Empty {} + | ^^^^^^^^^^^^^^^ From 54b96bfe28db1c79606012f91ca7f750bbceb752 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 20 2020 22:11:35 +0000 Subject: [PATCH 190/2232] Merge pull request #81 from dtolnay/empty Disallow shared structs having 0 fields --- diff --git a/syntax/check.rs b/syntax/check.rs index 1fe3daf..742ecc2 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Ty1, Type, Types, Var}; -use proc_macro2::Ident; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var}; +use proc_macro2::{Delimiter, Group, Ident, TokenStream}; +use quote::quote; use syn::{Error, Result}; pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { @@ -53,6 +54,9 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { for api in apis { match api { Api::Struct(strct) => { + if strct.fields.is_empty() { + errors.push(struct_empty(strct)); + } for field in &strct.fields { if is_unsized(&field.ty, types) { errors.push(field_by_value(field, types)); @@ -197,6 +201,14 @@ fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error { Error::new_spanned(unique_ptr, "unsupported unique_ptr target type") } +fn struct_empty(strct: &Struct) -> Error { + let struct_token = strct.struct_token; + let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); + brace_token.set_span(strct.brace_token.span); + let span = quote!(#struct_token #brace_token); + Error::new_spanned(span, "structs without any fields are not supported") +} + fn field_by_value(field: &Var, types: &Types) -> Error { let desc = describe(&field.ty, types); let message = format!("using {} by value is not supported", desc); diff --git a/syntax/mod.rs b/syntax/mod.rs index 7d2ca19..38e21b0 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,7 +13,7 @@ mod tokens; pub mod types; use proc_macro2::{Ident, Span, TokenStream}; -use syn::{LitStr, Token}; +use syn::{token::Brace, LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -40,6 +40,7 @@ pub struct Struct { pub derives: Vec, pub struct_token: Token![struct], pub ident: Ident, + pub brace_token: Brace, pub fields: Vec, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 3d35f78..a00f8dd 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -46,26 +46,32 @@ fn parse_struct(item: ItemStruct) -> Result { let mut derives = Vec::new(); attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?; check_reserved_name(&item.ident)?; - match item.fields { - Fields::Named(fields) => Ok(Api::Struct(Struct { - doc, - derives, - struct_token: item.struct_token, - ident: item.ident, - fields: fields - .named - .into_iter() - .map(|field| { - Ok(Var { - ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, - }) + + let fields = match item.fields { + Fields::Named(fields) => fields, + Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")), + Fields::Unnamed(_) => { + return Err(Error::new_spanned(item, "tuple structs are not supported")) + } + }; + + Ok(Api::Struct(Struct { + doc, + derives, + struct_token: item.struct_token, + ident: item.ident, + brace_token: fields.brace_token, + fields: fields + .named + .into_iter() + .map(|field| { + Ok(Var { + ident: field.ident.unwrap(), + ty: parse_type(&field.ty)?, }) - .collect::>()?, - })), - Fields::Unit => Err(Error::new_spanned(item, "unit structs are not supported")), - Fields::Unnamed(_) => Err(Error::new_spanned(item, "tuple structs are not supported")), - } + }) + .collect::>()?, + })) } fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { diff --git a/tests/ui/empty_struct.rs b/tests/ui/empty_struct.rs new file mode 100644 index 0000000..060cfe0 --- /dev/null +++ b/tests/ui/empty_struct.rs @@ -0,0 +1,6 @@ +#[cxx::bridge] +mod ffi { + struct Empty {} +} + +fn main() {} diff --git a/tests/ui/empty_struct.stderr b/tests/ui/empty_struct.stderr new file mode 100644 index 0000000..612476b --- /dev/null +++ b/tests/ui/empty_struct.stderr @@ -0,0 +1,5 @@ +error: structs without any fields are not supported + --> $DIR/empty_struct.rs:3:5 + | +3 | struct Empty {} + | ^^^^^^^^^^^^^^^ From 84849300714aab955eae57285327d53c8862ba5b Mon Sep 17 00:00:00 2001 From: Myron Ahn Date: Mar 25 2020 15:39:00 +0000 Subject: [PATCH 191/2232] Result now works for UniquePtr and other types --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e11b2d3..69b7fb5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -225,10 +225,24 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types #local_name(#(#vars),*) } }; - let expr = efn - .ret - .as_ref() - .and_then(|ret| match ret { + let expr = if efn.throws { + efn.ret.as_ref().and_then(|ret| match ret { + Type::Ident(ident) if ident == RustString => { + Some(quote!(#call.map(|r| r.into_string()))) + } + Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), + Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == RustString => { + Some(quote!(#call.map(|r| r.as_string()))) + } + _ => None, + }, + Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), + _ => None, + }) + } else { + efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), @@ -239,7 +253,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::Str(_) => Some(quote!(#call.as_str())), _ => None, }) - .unwrap_or(call); + } + .unwrap_or(call); quote! { #doc pub fn #ident(#(#args),*) #ret { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index bd9d4df..5d0598e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -36,6 +36,8 @@ pub mod ffi { fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; fn c_fail_return_primitive() -> Result; + fn c_try_return_string() -> Result>; + fn c_fail_return_string() -> Result>; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 184e8aa..90538b9 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -98,6 +98,14 @@ size_t c_try_return_primitive() { return 2020; } size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } +std::unique_ptr c_try_return_string() { + return std::unique_ptr(new std::string("ok")); +} + +std::unique_ptr c_fail_return_string() { + throw std::logic_error("logic error getting string"); +} + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6a5f802..7f45287 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -39,5 +39,7 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_try_return_void(); size_t c_try_return_primitive(); size_t c_fail_return_primitive(); +std::unique_ptr c_try_return_string(); +std::unique_ptr c_fail_return_string(); } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index d86ebd2..9f1ea24 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -45,6 +45,18 @@ fn test_c_return() { "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); + assert_eq!( + "ok", + ffi::c_try_return_string() + .unwrap() + .as_ref() + .unwrap() + .to_string() + ); + assert_eq!( + "logic error getting string", + ffi::c_fail_return_string().unwrap_err().what(), + ); } #[test] From a8520d417fa25e19fc2427213c18295ee835d1e4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 19:14:19 +0000 Subject: [PATCH 192/2232] Merge pull request #83 from myronahn/feature/result-quick-fix Make Result work with UniquePtr, RustBox, RustString, Str --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e11b2d3..69b7fb5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -225,10 +225,24 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types #local_name(#(#vars),*) } }; - let expr = efn - .ret - .as_ref() - .and_then(|ret| match ret { + let expr = if efn.throws { + efn.ret.as_ref().and_then(|ret| match ret { + Type::Ident(ident) if ident == RustString => { + Some(quote!(#call.map(|r| r.into_string()))) + } + Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), + Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == RustString => { + Some(quote!(#call.map(|r| r.as_string()))) + } + _ => None, + }, + Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), + _ => None, + }) + } else { + efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), @@ -239,7 +253,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::Str(_) => Some(quote!(#call.as_str())), _ => None, }) - .unwrap_or(call); + } + .unwrap_or(call); quote! { #doc pub fn #ident(#(#args),*) #ret { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index bd9d4df..5d0598e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -36,6 +36,8 @@ pub mod ffi { fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; fn c_fail_return_primitive() -> Result; + fn c_try_return_string() -> Result>; + fn c_fail_return_string() -> Result>; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 184e8aa..90538b9 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -98,6 +98,14 @@ size_t c_try_return_primitive() { return 2020; } size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } +std::unique_ptr c_try_return_string() { + return std::unique_ptr(new std::string("ok")); +} + +std::unique_ptr c_fail_return_string() { + throw std::logic_error("logic error getting string"); +} + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6a5f802..7f45287 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -39,5 +39,7 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_try_return_void(); size_t c_try_return_primitive(); size_t c_fail_return_primitive(); +std::unique_ptr c_try_return_string(); +std::unique_ptr c_fail_return_string(); } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index d86ebd2..9f1ea24 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -45,6 +45,18 @@ fn test_c_return() { "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); + assert_eq!( + "ok", + ffi::c_try_return_string() + .unwrap() + .as_ref() + .unwrap() + .to_string() + ); + assert_eq!( + "logic error getting string", + ffi::c_fail_return_string().unwrap_err().what(), + ); } #[test] From f90ce8585ba5406e604e0aa5954b87f1ab2d8e25 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 19:15:18 +0000 Subject: [PATCH 193/2232] Remove trailing whitespace from tests from PR 83 --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 90538b9..6ca27db 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -102,8 +102,8 @@ std::unique_ptr c_try_return_string() { return std::unique_ptr(new std::string("ok")); } -std::unique_ptr c_fail_return_string() { - throw std::logic_error("logic error getting string"); +std::unique_ptr c_fail_return_string() { + throw std::logic_error("logic error getting string"); } extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { From d930a79e6fb665789117800b55e8ab96d4e61357 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 19:24:40 +0000 Subject: [PATCH 194/2232] Write CxxString fmt impls in terms of to_string_lossy --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index f2cca5b..6b69435 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -80,12 +80,12 @@ impl CxxString { impl Display for CxxString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&String::from_utf8_lossy(self.as_bytes()), f) + Display::fmt(self.to_string_lossy().as_ref(), f) } } impl Debug for CxxString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(&String::from_utf8_lossy(self.as_bytes()), f) + Debug::fmt(self.to_string_lossy().as_ref(), f) } } From 42ebfa2d5e0968078d8a29c40fe09ddbd545b49f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 19:26:22 +0000 Subject: [PATCH 195/2232] Add some PartialEq impls for CxxString --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 6b69435..d3d128c 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -89,3 +89,21 @@ impl Debug for CxxString { Debug::fmt(self.to_string_lossy().as_ref(), f) } } + +impl PartialEq for CxxString { + fn eq(&self, other: &CxxString) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for str { + fn eq(&self, other: &CxxString) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for CxxString { + fn eq(&self, other: &str) -> bool { + self.as_bytes() == other.as_bytes() + } +} diff --git a/tests/test.rs b/tests/test.rs index 9f1ea24..87a18ad 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -45,14 +45,7 @@ fn test_c_return() { "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); - assert_eq!( - "ok", - ffi::c_try_return_string() - .unwrap() - .as_ref() - .unwrap() - .to_string() - ); + assert_eq!("ok", ffi::c_try_return_string().unwrap().as_ref().unwrap()); assert_eq!( "logic error getting string", ffi::c_fail_return_string().unwrap_err().what(), From 9964262630a98b2f6b6d3309b306b2bb0af70410 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 23:44:11 +0000 Subject: [PATCH 196/2232] Implement and test some more fallible return types --- diff --git a/gen/write.rs b/gen/write.rs index a4e2eb2..72962f2 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -297,7 +297,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if efn.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type(out, &efn.ret, types); + write_extern_return_type_space(out, &efn.ret, types); } for name in out.namespace.clone() { write!(out, "{}$", name); @@ -317,7 +317,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if !efn.args.is_empty() { write!(out, ", "); } - write_return_type(out, &efn.ret); + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); write!(out, "*return$"); } writeln!(out, ") noexcept {{"); @@ -340,15 +340,15 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } if indirect_return { write!(out, "new (return$) "); - write_type(out, efn.ret.as_ref().unwrap()); + write_indirect_return_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); - } else if let Some(ret) = &efn.ret { + } else if efn.ret.is_some() { write!(out, "return "); - match ret { - Type::Ref(_) => write!(out, "&"), - Type::Str(_) => write!(out, "::rust::Str::Repr("), - _ => {} - } + } + match &efn.ret { + Some(Type::Ref(_)) => write!(out, "&"), + Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + _ => {} } write!(out, "{}$(", efn.ident); for (i, arg) in efn.args.iter().enumerate() { @@ -378,7 +378,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) => write!(out, ")"), + Some(Type::Str(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -405,7 +405,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { if efn.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type(out, &efn.ret, types); + write_extern_return_type_space(out, &efn.ret, types); } for name in out.namespace.clone() { write!(out, "{}$", name); @@ -542,7 +542,34 @@ fn indirect_return(efn: &ExternFn, types: &Types) -> bool { .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) } -fn write_extern_return_type(out: &mut OutFile, ty: &Option, types: &Types) { +fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::RustBox(ty) | Type::UniquePtr(ty) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Type::Ref(ty) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } + Type::Str(_) => write!(out, "::rust::Str::Repr"), + _ => write_type(out, ty), + } +} + +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { + write_indirect_return_type(out, ty); + match ty { + Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} + Type::Str(_) => write!(out, " "), + _ => write_space_after_type(out, ty), + } +} + +fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { match ty { Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { write_type_space(out, &ty.inner); @@ -623,6 +650,10 @@ fn write_type(out: &mut OutFile, ty: &Type) { fn write_type_space(out: &mut OutFile, ty: &Type) { write_type(out, ty); + write_space_after_type(out, ty); +} + +fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), Type::Ref(_) => {} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 5d0598e..492fc91 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -36,8 +36,11 @@ pub mod ffi { fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; fn c_fail_return_primitive() -> Result; - fn c_try_return_string() -> Result>; - fn c_fail_return_string() -> Result>; + fn c_try_return_box() -> Result>; + fn c_try_return_ref(s: &String) -> Result<&String>; + fn c_try_return_str(s: &str) -> Result<&str>; + fn c_try_return_rust_string() -> Result; + fn c_try_return_unique_ptr_string() -> Result>; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 6ca27db..6384ac1 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -98,12 +98,16 @@ size_t c_try_return_primitive() { return 2020; } size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } -std::unique_ptr c_try_return_string() { - return std::unique_ptr(new std::string("ok")); -} +rust::Box c_try_return_box() { return c_return_box(); } + +const rust::String &c_try_return_ref(const rust::String &s) { return s; } + +rust::Str c_try_return_str(rust::Str s) { return s; } + +rust::String c_try_return_rust_string() { return c_return_rust_string(); } -std::unique_ptr c_fail_return_string() { - throw std::logic_error("logic error getting string"); +std::unique_ptr c_try_return_unique_ptr_string() { + return c_return_unique_ptr_string(); } extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7f45287..e0e7624 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -39,7 +39,10 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_try_return_void(); size_t c_try_return_primitive(); size_t c_fail_return_primitive(); -std::unique_ptr c_try_return_string(); -std::unique_ptr c_fail_return_string(); +rust::Box c_try_return_box(); +const rust::String &c_try_return_ref(const rust::String &); +rust::Str c_try_return_str(rust::Str); +rust::String c_try_return_rust_string(); +std::unique_ptr c_try_return_unique_ptr_string(); } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index 87a18ad..6a6fc3e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -38,17 +38,26 @@ fn test_c_return() { .to_str() .unwrap() ); +} +#[test] +fn test_c_try_return() { assert_eq!((), ffi::c_try_return_void().unwrap()); assert_eq!(2020, ffi::c_try_return_primitive().unwrap()); assert_eq!( "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); - assert_eq!("ok", ffi::c_try_return_string().unwrap().as_ref().unwrap()); + assert_eq!(2020, *ffi::c_try_return_box().unwrap()); + assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); + assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); + assert_eq!("2020", ffi::c_try_return_rust_string().unwrap()); assert_eq!( - "logic error getting string", - ffi::c_fail_return_string().unwrap_err().what(), + "2020", + ffi::c_try_return_unique_ptr_string() + .unwrap() + .as_ref() + .unwrap() ); } From d4e6830d40756c822892409506e666e560f23203 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 25 2020 23:59:02 +0000 Subject: [PATCH 197/2232] Split up typecheck logic to functions --- diff --git a/syntax/check.rs b/syntax/check.rs index 742ecc2..f5ccba9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -5,47 +5,14 @@ use quote::quote; use syn::{Error, Result}; pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { - let mut errors = Vec::new(); + let ref mut errors = Vec::new(); for ty in types { match ty { - Type::Ident(ident) => { - if Atom::from(ident).is_none() - && !types.structs.contains_key(ident) - && !types.cxx.contains(ident) - && !types.rust.contains(ident) - { - errors.push(unsupported_type(ident)); - } - } - Type::RustBox(ptr) => { - if let Type::Ident(ident) = &ptr.inner { - if types.cxx.contains(ident) { - errors.push(unsupported_cxx_type_in_box(ptr)); - } - if Atom::from(ident).is_none() { - continue; - } - } - errors.push(unsupported_box_target(ptr)); - } - Type::UniquePtr(ptr) => { - if let Type::Ident(ident) = &ptr.inner { - if types.rust.contains(ident) { - errors.push(unsupported_rust_type_in_unique_ptr(ptr)); - } - match Atom::from(ident) { - None | Some(CxxString) => continue, - _ => {} - } - } - errors.push(unsupported_unique_ptr_target(ptr)); - } - Type::Ref(ty) => { - if let Type::Void(_) = ty.inner { - errors.push(unsupported_reference_type(ty)); - } - } + Type::Ident(ident) => check_type_ident(errors, types, ident), + Type::RustBox(ptr) => check_type_box(errors, types, ptr), + Type::UniquePtr(ptr) => check_type_unique_ptr(errors, types, ptr), + Type::Ref(ty) => check_type_ref(errors, ty), Type::Fn(_) => errors.push(unimplemented_fn_type(ty)), _ => {} } @@ -53,28 +20,8 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { for api in apis { match api { - Api::Struct(strct) => { - if strct.fields.is_empty() { - errors.push(struct_empty(strct)); - } - for field in &strct.fields { - if is_unsized(&field.ty, types) { - errors.push(field_by_value(field, types)); - } - } - } - Api::CxxFunction(efn) | Api::RustFunction(efn) => { - for arg in &efn.args { - if is_unsized(&arg.ty, types) { - errors.push(argument_by_value(arg, types)); - } - } - if let Some(ty) = &efn.ret { - if is_unsized(ty, types) { - errors.push(return_by_value(ty, types)); - } - } - } + Api::Struct(strct) => check_api_struct(errors, types, strct), + Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(errors, types, efn), _ => {} } } @@ -88,9 +35,9 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { } } - ident::check_all(apis, &mut errors); + ident::check_all(apis, errors); - let mut iter = errors.into_iter(); + let mut iter = errors.drain(..); let mut all_errors = match iter.next() { Some(err) => err, None => return Ok(()), @@ -101,13 +48,69 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { Err(all_errors) } -fn is_unsized(ty: &Type, types: &Types) -> bool { - let ident = match ty { - Type::Ident(ident) => ident, - Type::Void(_) => return true, - _ => return false, - }; - ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) +fn check_type_ident(errors: &mut Vec, types: &Types, ident: &Ident) { + if Atom::from(ident).is_none() + && !types.structs.contains_key(ident) + && !types.cxx.contains(ident) + && !types.rust.contains(ident) + { + errors.push(unsupported_type(ident)); + } +} + +fn check_type_box(errors: &mut Vec, types: &Types, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if types.cxx.contains(ident) { + errors.push(unsupported_cxx_type_in_box(ptr)); + } + if Atom::from(ident).is_none() { + return; + } + } + errors.push(unsupported_box_target(ptr)); +} + +fn check_type_unique_ptr(errors: &mut Vec, types: &Types, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if types.rust.contains(ident) { + errors.push(unsupported_rust_type_in_unique_ptr(ptr)); + } + match Atom::from(ident) { + None | Some(CxxString) => return, + _ => {} + } + } + errors.push(unsupported_unique_ptr_target(ptr)); +} + +fn check_type_ref(errors: &mut Vec, ty: &Ref) { + if let Type::Void(_) = ty.inner { + errors.push(unsupported_reference_type(ty)); + } +} + +fn check_api_struct(errors: &mut Vec, types: &Types, strct: &Struct) { + if strct.fields.is_empty() { + errors.push(struct_empty(strct)); + } + for field in &strct.fields { + if is_unsized(&field.ty, types) { + errors.push(field_by_value(field, types)); + } + } +} + +fn check_api_fn(errors: &mut Vec, types: &Types, efn: &ExternFn) { + for arg in &efn.args { + if is_unsized(&arg.ty, types) { + errors.push(argument_by_value(arg, types)); + } + } + if let Some(ty) = &efn.ret { + if is_unsized(ty, types) { + errors.push(return_by_value(ty, types)); + } + } } fn check_mut_return_restriction(efn: &ExternFn) -> Result<()> { @@ -153,6 +156,15 @@ fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> { } } +fn is_unsized(ty: &Type, types: &Types) -> bool { + let ident = match ty { + Type::Ident(ident) => ident, + Type::Void(_) => return true, + _ => return false, + }; + ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) +} + fn describe(ty: &Type, types: &Types) -> String { match ty { Type::Ident(ident) => { From 26a2a1deda5da262a48a01d017cf99aca5f089c9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 26 2020 00:35:17 +0000 Subject: [PATCH 198/2232] Collect typecheck context into a struct --- diff --git a/syntax/check.rs b/syntax/check.rs index f5ccba9..5a3590f 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -4,139 +4,144 @@ use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::quote; use syn::{Error, Result}; +struct Check<'a> { + apis: &'a [Api], + types: &'a Types<'a>, + errors: &'a mut Vec, +} + pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { - let ref mut errors = Vec::new(); + let mut errors = Vec::new(); + let mut cx = Check { + apis, + types, + errors: &mut errors, + }; + do_typecheck(&mut cx); + combine_errors(errors) +} - for ty in types { +fn do_typecheck(cx: &mut Check) { + for ty in cx.types { match ty { - Type::Ident(ident) => check_type_ident(errors, types, ident), - Type::RustBox(ptr) => check_type_box(errors, types, ptr), - Type::UniquePtr(ptr) => check_type_unique_ptr(errors, types, ptr), - Type::Ref(ty) => check_type_ref(errors, ty), - Type::Fn(_) => errors.push(unimplemented_fn_type(ty)), + Type::Ident(ident) => check_type_ident(cx, ident), + Type::RustBox(ptr) => check_type_box(cx, ptr), + Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), + Type::Ref(ty) => check_type_ref(cx, ty), + Type::Fn(_) => cx.errors.push(unimplemented_fn_type(ty)), _ => {} } } - for api in apis { + for api in cx.apis { match api { - Api::Struct(strct) => check_api_struct(errors, types, strct), - Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(errors, types, efn), + Api::Struct(strct) => check_api_struct(cx, strct), + Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} } } - for api in apis { + for api in cx.apis { if let Api::CxxFunction(efn) = api { - errors.extend(check_mut_return_restriction(efn).err()); + check_mut_return_restriction(cx, efn); } if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api { - errors.extend(check_multiple_arg_lifetimes(efn).err()); + check_multiple_arg_lifetimes(cx, efn); } } - ident::check_all(apis, errors); - - let mut iter = 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) + ident::check_all(cx.apis, cx.errors); } -fn check_type_ident(errors: &mut Vec, types: &Types, ident: &Ident) { +fn check_type_ident(cx: &mut Check, ident: &Ident) { if Atom::from(ident).is_none() - && !types.structs.contains_key(ident) - && !types.cxx.contains(ident) - && !types.rust.contains(ident) + && !cx.types.structs.contains_key(ident) + && !cx.types.cxx.contains(ident) + && !cx.types.rust.contains(ident) { - errors.push(unsupported_type(ident)); + cx.errors.push(unsupported_type(ident)); } } -fn check_type_box(errors: &mut Vec, types: &Types, ptr: &Ty1) { +fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if types.cxx.contains(ident) { - errors.push(unsupported_cxx_type_in_box(ptr)); + if cx.types.cxx.contains(ident) { + cx.errors.push(unsupported_cxx_type_in_box(ptr)); } if Atom::from(ident).is_none() { return; } } - errors.push(unsupported_box_target(ptr)); + cx.errors.push(unsupported_box_target(ptr)); } -fn check_type_unique_ptr(errors: &mut Vec, types: &Types, ptr: &Ty1) { +fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if types.rust.contains(ident) { - errors.push(unsupported_rust_type_in_unique_ptr(ptr)); + if cx.types.rust.contains(ident) { + cx.errors.push(unsupported_rust_type_in_unique_ptr(ptr)); } match Atom::from(ident) { None | Some(CxxString) => return, _ => {} } } - errors.push(unsupported_unique_ptr_target(ptr)); + cx.errors.push(unsupported_unique_ptr_target(ptr)); } -fn check_type_ref(errors: &mut Vec, ty: &Ref) { +fn check_type_ref(cx: &mut Check, ty: &Ref) { if let Type::Void(_) = ty.inner { - errors.push(unsupported_reference_type(ty)); + cx.errors.push(unsupported_reference_type(ty)); } } -fn check_api_struct(errors: &mut Vec, types: &Types, strct: &Struct) { +fn check_api_struct(cx: &mut Check, strct: &Struct) { if strct.fields.is_empty() { - errors.push(struct_empty(strct)); + cx.errors.push(struct_empty(strct)); } for field in &strct.fields { - if is_unsized(&field.ty, types) { - errors.push(field_by_value(field, types)); + if is_unsized(cx, &field.ty) { + cx.errors.push(field_by_value(field, cx.types)); } } } -fn check_api_fn(errors: &mut Vec, types: &Types, efn: &ExternFn) { +fn check_api_fn(cx: &mut Check, efn: &ExternFn) { for arg in &efn.args { - if is_unsized(&arg.ty, types) { - errors.push(argument_by_value(arg, types)); + if is_unsized(cx, &arg.ty) { + cx.errors.push(argument_by_value(arg, cx.types)); } } if let Some(ty) = &efn.ret { - if is_unsized(ty, types) { - errors.push(return_by_value(ty, types)); + if is_unsized(cx, ty) { + cx.errors.push(return_by_value(ty, cx.types)); } } } -fn check_mut_return_restriction(efn: &ExternFn) -> Result<()> { +fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(ty)) if ty.mutability.is_some() => {} - _ => return Ok(()), + _ => return, } for arg in &efn.args { if let Type::Ref(ty) = &arg.ty { if ty.mutability.is_some() { - return Ok(()); + return; } } } - Err(Error::new_spanned( + cx.errors.push(Error::new_spanned( efn, "&mut return type is not allowed unless there is a &mut argument", - )) + )); } -fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> { +fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(_)) => {} - _ => return Ok(()), + _ => return, } let mut reference_args = 0; @@ -146,23 +151,33 @@ fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> { } } - if reference_args == 1 { - Ok(()) - } else { - Err(Error::new_spanned( + if reference_args != 1 { + cx.errors.push(Error::new_spanned( efn, "functions that return a reference must take exactly one input reference", - )) + )); } } -fn is_unsized(ty: &Type, types: &Types) -> bool { +fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, Type::Void(_) => return true, _ => return false, }; - ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) + ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) +} + +fn combine_errors(errors: Vec) -> Result<()> { + let mut iter = errors.into_iter(); + let mut all_errors = match iter.next() { + Some(err) => err, + None => return Ok(()), + }; + for err in iter { + all_errors.combine(err); + } + Err(all_errors) } fn describe(ty: &Type, types: &Types) -> String { From a420f0141aca0c434de8427f1dbd061dfdda4883 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 26 2020 01:01:12 +0000 Subject: [PATCH 199/2232] Move error message construction into checking functions These were pulled out originally because error message construction was so verbose that it detracted from being able to follow the logic of the checks, but now that checks are broken up into finer granularity the messages can be inlined. --- diff --git a/syntax/check.rs b/syntax/check.rs index 5a3590f..256a193 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,7 +1,8 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; -use quote::quote; +use quote::{quote, ToTokens}; +use std::fmt::Display; use syn::{Error, Result}; struct Check<'a> { @@ -28,7 +29,7 @@ fn do_typecheck(cx: &mut Check) { Type::RustBox(ptr) => check_type_box(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), - Type::Fn(_) => cx.errors.push(unimplemented_fn_type(ty)), + Type::Fn(_) => cx.error(ty, "function pointer support is not implemented yet"), _ => {} } } @@ -53,54 +54,68 @@ fn do_typecheck(cx: &mut Check) { ident::check_all(cx.apis, cx.errors); } +impl Check<'_> { + fn error(&mut self, sp: impl ToTokens, msg: impl Display) { + self.errors.push(Error::new_spanned(sp, msg)); + } +} + fn check_type_ident(cx: &mut Check, ident: &Ident) { if Atom::from(ident).is_none() && !cx.types.structs.contains_key(ident) && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.errors.push(unsupported_type(ident)); + cx.error(ident, "unsupported type"); } } fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.cxx.contains(ident) { - cx.errors.push(unsupported_cxx_type_in_box(ptr)); + cx.error(ptr, error::BOX_CXX_TYPE.msg); } + if Atom::from(ident).is_none() { return; } } - cx.errors.push(unsupported_box_target(ptr)); + + cx.error(ptr, "unsupported target type of Box"); } fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(ident) { - cx.errors.push(unsupported_rust_type_in_unique_ptr(ptr)); + cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); } + match Atom::from(ident) { None | Some(CxxString) => return, _ => {} } } - cx.errors.push(unsupported_unique_ptr_target(ptr)); + + cx.error(ptr, "unsupported unique_ptr target type"); } fn check_type_ref(cx: &mut Check, ty: &Ref) { if let Type::Void(_) = ty.inner { - cx.errors.push(unsupported_reference_type(ty)); + cx.error(ty, "unsupported reference type"); } } fn check_api_struct(cx: &mut Check, strct: &Struct) { if strct.fields.is_empty() { - cx.errors.push(struct_empty(strct)); + let span = span_for_struct_error(strct); + cx.error(span, "structs without any fields are not supported"); } + for field in &strct.fields { if is_unsized(cx, &field.ty) { - cx.errors.push(field_by_value(field, cx.types)); + let desc = describe(cx, &field.ty); + let msg = format!("using {} by value is not supported", desc); + cx.error(field, msg); } } } @@ -108,12 +123,17 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { fn check_api_fn(cx: &mut Check, efn: &ExternFn) { for arg in &efn.args { if is_unsized(cx, &arg.ty) { - cx.errors.push(argument_by_value(arg, cx.types)); + 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 is_unsized(cx, ty) { - cx.errors.push(return_by_value(ty, cx.types)); + let desc = describe(cx, ty); + let msg = format!("returning {} by value is not supported", desc); + cx.error(ty, msg); } } } @@ -132,10 +152,10 @@ fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { } } - cx.errors.push(Error::new_spanned( + cx.error( efn, "&mut return type is not allowed unless there is a &mut argument", - )); + ); } fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { @@ -152,10 +172,10 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } if reference_args != 1 { - cx.errors.push(Error::new_spanned( + cx.error( efn, "functions that return a reference must take exactly one input reference", - )); + ); } } @@ -168,6 +188,13 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) } +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 combine_errors(errors: Vec) -> Result<()> { let mut iter = errors.into_iter(); let mut all_errors = match iter.next() { @@ -180,14 +207,14 @@ fn combine_errors(errors: Vec) -> Result<()> { Err(all_errors) } -fn describe(ty: &Type, types: &Types) -> String { +fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { - if types.structs.contains_key(ident) { + if cx.types.structs.contains_key(ident) { "struct".to_owned() - } else if types.cxx.contains(ident) { + } else if cx.types.cxx.contains(ident) { "C++ type".to_owned() - } else if types.rust.contains(ident) { + } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { "C++ string".to_owned() @@ -203,57 +230,3 @@ fn describe(ty: &Type, types: &Types) -> String { Type::Void(_) => "()".to_owned(), } } - -fn unsupported_type(ident: &Ident) -> Error { - Error::new(ident.span(), "unsupported type") -} - -fn unsupported_reference_type(ty: &Ref) -> Error { - Error::new_spanned(ty, "unsupported reference type") -} - -fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg) -} - -fn unsupported_box_target(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unsupported target type of Box") -} - -fn unsupported_rust_type_in_unique_ptr(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unique_ptr of a Rust type is not supported yet") -} - -fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unsupported unique_ptr target type") -} - -fn struct_empty(strct: &Struct) -> Error { - let struct_token = strct.struct_token; - let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); - brace_token.set_span(strct.brace_token.span); - let span = quote!(#struct_token #brace_token); - Error::new_spanned(span, "structs without any fields are not supported") -} - -fn field_by_value(field: &Var, types: &Types) -> Error { - let desc = describe(&field.ty, types); - let message = format!("using {} by value is not supported", desc); - Error::new_spanned(field, message) -} - -fn argument_by_value(arg: &Var, types: &Types) -> Error { - let desc = describe(&arg.ty, types); - let message = format!("passing {} by value is not supported", desc); - Error::new_spanned(arg, message) -} - -fn return_by_value(ty: &Type, types: &Types) -> Error { - let desc = describe(ty, types); - let message = format!("returning {} by value is not supported", desc); - Error::new_spanned(ty, message) -} - -fn unimplemented_fn_type(ty: &Type) -> Error { - Error::new_spanned(ty, "function pointer support is not implemented yet") -} From cd0793f0f1b461435ea04e2ae83fed1e03388d64 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 26 2020 01:12:15 +0000 Subject: [PATCH 200/2232] Merge pull request #84 from dtolnay/check Typecheck refactor --- diff --git a/syntax/check.rs b/syntax/check.rs index 742ecc2..256a193 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,139 +1,167 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var}; +use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; -use quote::quote; +use quote::{quote, ToTokens}; +use std::fmt::Display; use syn::{Error, Result}; +struct Check<'a> { + apis: &'a [Api], + types: &'a Types<'a>, + errors: &'a mut Vec, +} + pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { let mut errors = Vec::new(); + let mut cx = Check { + apis, + types, + errors: &mut errors, + }; + do_typecheck(&mut cx); + combine_errors(errors) +} - for ty in types { +fn do_typecheck(cx: &mut Check) { + for ty in cx.types { match ty { - Type::Ident(ident) => { - if Atom::from(ident).is_none() - && !types.structs.contains_key(ident) - && !types.cxx.contains(ident) - && !types.rust.contains(ident) - { - errors.push(unsupported_type(ident)); - } - } - Type::RustBox(ptr) => { - if let Type::Ident(ident) = &ptr.inner { - if types.cxx.contains(ident) { - errors.push(unsupported_cxx_type_in_box(ptr)); - } - if Atom::from(ident).is_none() { - continue; - } - } - errors.push(unsupported_box_target(ptr)); - } - Type::UniquePtr(ptr) => { - if let Type::Ident(ident) = &ptr.inner { - if types.rust.contains(ident) { - errors.push(unsupported_rust_type_in_unique_ptr(ptr)); - } - match Atom::from(ident) { - None | Some(CxxString) => continue, - _ => {} - } - } - errors.push(unsupported_unique_ptr_target(ptr)); - } - Type::Ref(ty) => { - if let Type::Void(_) = ty.inner { - errors.push(unsupported_reference_type(ty)); - } - } - Type::Fn(_) => errors.push(unimplemented_fn_type(ty)), + Type::Ident(ident) => check_type_ident(cx, ident), + Type::RustBox(ptr) => check_type_box(cx, ptr), + Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), + Type::Ref(ty) => check_type_ref(cx, ty), + Type::Fn(_) => cx.error(ty, "function pointer support is not implemented yet"), _ => {} } } - for api in apis { + for api in cx.apis { match api { - Api::Struct(strct) => { - if strct.fields.is_empty() { - errors.push(struct_empty(strct)); - } - for field in &strct.fields { - if is_unsized(&field.ty, types) { - errors.push(field_by_value(field, types)); - } - } - } - Api::CxxFunction(efn) | Api::RustFunction(efn) => { - for arg in &efn.args { - if is_unsized(&arg.ty, types) { - errors.push(argument_by_value(arg, types)); - } - } - if let Some(ty) = &efn.ret { - if is_unsized(ty, types) { - errors.push(return_by_value(ty, types)); - } - } - } + Api::Struct(strct) => check_api_struct(cx, strct), + Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} } } - for api in apis { + for api in cx.apis { if let Api::CxxFunction(efn) = api { - errors.extend(check_mut_return_restriction(efn).err()); + check_mut_return_restriction(cx, efn); } if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api { - errors.extend(check_multiple_arg_lifetimes(efn).err()); + check_multiple_arg_lifetimes(cx, efn); } } - ident::check_all(apis, &mut errors); + ident::check_all(cx.apis, cx.errors); +} - let mut iter = errors.into_iter(); - let mut all_errors = match iter.next() { - Some(err) => err, - None => return Ok(()), - }; - for err in iter { - all_errors.combine(err); +impl Check<'_> { + fn error(&mut self, sp: impl ToTokens, msg: impl Display) { + self.errors.push(Error::new_spanned(sp, msg)); } - Err(all_errors) } -fn is_unsized(ty: &Type, types: &Types) -> bool { - let ident = match ty { - Type::Ident(ident) => ident, - Type::Void(_) => return true, - _ => return false, - }; - ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident) +fn check_type_ident(cx: &mut Check, ident: &Ident) { + if Atom::from(ident).is_none() + && !cx.types.structs.contains_key(ident) + && !cx.types.cxx.contains(ident) + && !cx.types.rust.contains(ident) + { + cx.error(ident, "unsupported type"); + } } -fn check_mut_return_restriction(efn: &ExternFn) -> Result<()> { +fn check_type_box(cx: &mut Check, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if cx.types.cxx.contains(ident) { + cx.error(ptr, error::BOX_CXX_TYPE.msg); + } + + if Atom::from(ident).is_none() { + return; + } + } + + cx.error(ptr, "unsupported target type of Box"); +} + +fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if cx.types.rust.contains(ident) { + cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); + } + + match Atom::from(ident) { + None | Some(CxxString) => return, + _ => {} + } + } + + cx.error(ptr, "unsupported unique_ptr target type"); +} + +fn check_type_ref(cx: &mut Check, ty: &Ref) { + if let Type::Void(_) = ty.inner { + cx.error(ty, "unsupported reference type"); + } +} + +fn check_api_struct(cx: &mut Check, strct: &Struct) { + if strct.fields.is_empty() { + let span = span_for_struct_error(strct); + cx.error(span, "structs without any fields are not supported"); + } + + for field in &strct.fields { + 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_fn(cx: &mut Check, efn: &ExternFn) { + for arg in &efn.args { + 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 is_unsized(cx, ty) { + let desc = describe(cx, ty); + let msg = format!("returning {} by value is not supported", desc); + cx.error(ty, msg); + } + } +} + +fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(ty)) if ty.mutability.is_some() => {} - _ => return Ok(()), + _ => return, } for arg in &efn.args { if let Type::Ref(ty) = &arg.ty { if ty.mutability.is_some() { - return Ok(()); + return; } } } - Err(Error::new_spanned( + cx.error( efn, "&mut return type is not allowed unless there is a &mut argument", - )) + ); } -fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> { +fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(_)) => {} - _ => return Ok(()), + _ => return, } let mut reference_args = 0; @@ -143,24 +171,50 @@ fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> { } } - if reference_args == 1 { - Ok(()) - } else { - Err(Error::new_spanned( + if reference_args != 1 { + cx.error( efn, "functions that return a reference must take exactly one input reference", - )) + ); + } +} + +fn is_unsized(cx: &mut Check, ty: &Type) -> bool { + let ident = match ty { + Type::Ident(ident) => ident, + Type::Void(_) => return true, + _ => return false, + }; + ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) +} + +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 combine_errors(errors: Vec) -> Result<()> { + let mut iter = errors.into_iter(); + let mut all_errors = match iter.next() { + Some(err) => err, + None => return Ok(()), + }; + for err in iter { + all_errors.combine(err); } + Err(all_errors) } -fn describe(ty: &Type, types: &Types) -> String { +fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { - if types.structs.contains_key(ident) { + if cx.types.structs.contains_key(ident) { "struct".to_owned() - } else if types.cxx.contains(ident) { + } else if cx.types.cxx.contains(ident) { "C++ type".to_owned() - } else if types.rust.contains(ident) { + } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { "C++ string".to_owned() @@ -176,57 +230,3 @@ fn describe(ty: &Type, types: &Types) -> String { Type::Void(_) => "()".to_owned(), } } - -fn unsupported_type(ident: &Ident) -> Error { - Error::new(ident.span(), "unsupported type") -} - -fn unsupported_reference_type(ty: &Ref) -> Error { - Error::new_spanned(ty, "unsupported reference type") -} - -fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg) -} - -fn unsupported_box_target(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unsupported target type of Box") -} - -fn unsupported_rust_type_in_unique_ptr(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unique_ptr of a Rust type is not supported yet") -} - -fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error { - Error::new_spanned(unique_ptr, "unsupported unique_ptr target type") -} - -fn struct_empty(strct: &Struct) -> Error { - let struct_token = strct.struct_token; - let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); - brace_token.set_span(strct.brace_token.span); - let span = quote!(#struct_token #brace_token); - Error::new_spanned(span, "structs without any fields are not supported") -} - -fn field_by_value(field: &Var, types: &Types) -> Error { - let desc = describe(&field.ty, types); - let message = format!("using {} by value is not supported", desc); - Error::new_spanned(field, message) -} - -fn argument_by_value(arg: &Var, types: &Types) -> Error { - let desc = describe(&arg.ty, types); - let message = format!("passing {} by value is not supported", desc); - Error::new_spanned(arg, message) -} - -fn return_by_value(ty: &Type, types: &Types) -> Error { - let desc = describe(ty, types); - let message = format!("returning {} by value is not supported", desc); - Error::new_spanned(ty, message) -} - -fn unimplemented_fn_type(ty: &Type) -> Error { - Error::new_spanned(ty, "function pointer support is not implemented yet") -} From d7e1f1e519dd3ea66fe98a88e6e3b81cc3fa085a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 26 2020 03:18:39 +0000 Subject: [PATCH 201/2232] Split function pointer error message into specific cases --- diff --git a/syntax/check.rs b/syntax/check.rs index 256a193..b44d0cc 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,7 +29,6 @@ fn do_typecheck(cx: &mut Check) { Type::RustBox(ptr) => check_type_box(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), - Type::Fn(_) => cx.error(ty, "function pointer support is not implemented yet"), _ => {} } } @@ -100,9 +99,12 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { } fn check_type_ref(cx: &mut Check, ty: &Ref) { - if let Type::Void(_) = ty.inner { - cx.error(ty, "unsupported reference type"); + match ty.inner { + Type::Fn(_) | Type::Void(_) => {} + _ => return, } + + cx.error(ty, "unsupported reference type"); } fn check_api_struct(cx: &mut Check, strct: &Struct) { @@ -117,6 +119,12 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { let msg = format!("using {} by value is not supported", desc); cx.error(field, msg); } + if let Type::Fn(_) = field.ty { + cx.error( + field, + "function pointers in a struct field are not implemented yet", + ); + } } } @@ -127,6 +135,12 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { let msg = format!("passing {} by value is not supported", desc); cx.error(arg, msg); } + if let Type::Fn(_) = arg.ty { + cx.error( + arg, + "passing a function pointer argument is not implemented yet", + ); + } } if let Some(ty) = &efn.ret { @@ -135,6 +149,9 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { let msg = format!("returning {} by value is not supported", desc); cx.error(ty, msg); } + if let Type::Fn(_) = ty { + cx.error(ty, "returning a function pointer is not implemented yet"); + } } } From d140274c1078152ee57914f48de96b5ea74abecf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 26 2020 05:21:42 +0000 Subject: [PATCH 202/2232] Emit bitcopy forward declaration needed by String constructor --- diff --git a/gen/write.rs b/gen/write.rs index 72962f2..a17f8f7 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -183,6 +183,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "// #include \"rust/cxx.h\""); } + if needs_rust_string { + out.next_section(); + writeln!(out, "struct unsafe_bitcopy_t;"); + } + write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); From 3c19e53ad1ff1808dc9e3d41ca88c8d1eb5f38f9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 03:32:14 +0000 Subject: [PATCH 203/2232] Enable Namespace to be used in format_ident --- diff --git a/macro/src/namespace.rs b/macro/src/namespace.rs index 678e7e0..d2b3e1c 100644 --- a/macro/src/namespace.rs +++ b/macro/src/namespace.rs @@ -1,4 +1,5 @@ use crate::syntax::ident; +use quote::IdentFragment; use std::fmt::{self, Display}; use syn::parse::{Parse, ParseStream, Result}; use syn::{Path, Token}; @@ -37,3 +38,9 @@ impl Display for Namespace { Ok(()) } } + +impl IdentFragment for Namespace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} From 754e21c678efb3d8442d21c3af30b2f3554cef76 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 04:31:26 +0000 Subject: [PATCH 204/2232] Add a Namespace type in gen --- diff --git a/gen/mod.rs b/gen/mod.rs index 9a34705..8006898 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -3,10 +3,12 @@ mod error; pub(super) mod include; +mod namespace; pub(super) mod out; mod write; use self::error::format_err; +use self::namespace::Namespace; use self::out::OutFile; use crate::syntax::{self, check, ident, Types}; use quote::quote; @@ -32,7 +34,7 @@ pub(super) enum Error { } struct Input { - namespace: Vec, + namespace: Namespace, module: Vec, } @@ -86,10 +88,9 @@ fn find_bridge_mod(syntax: File) -> Result { ))); } }; - return Ok(Input { - namespace: parse_args(attr)?, - module, - }); + let namespace_segments = parse_args(attr)?; + let namespace = Namespace::new(namespace_segments); + return Ok(Input { namespace, module }); } } } diff --git a/gen/namespace.rs b/gen/namespace.rs new file mode 100644 index 0000000..c644352 --- /dev/null +++ b/gen/namespace.rs @@ -0,0 +1,44 @@ +use std::fmt::{self, Display}; +use std::slice::Iter; +use std::vec::IntoIter; + +#[derive(Clone)] +pub struct Namespace { + segments: Vec, +} + +impl Namespace { + pub fn new(segments: Vec) -> Self { + Namespace { segments } + } + + pub fn iter(&self) -> Iter { + self.segments.iter() + } +} + +impl Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for segment in self { + f.write_str(segment)?; + f.write_str("$")?; + } + Ok(()) + } +} + +impl<'a> IntoIterator for &'a Namespace { + type Item = &'a String; + type IntoIter = Iter<'a, String>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl IntoIterator for Namespace { + type Item = String; + type IntoIter = IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.segments.into_iter() + } +} diff --git a/gen/out.rs b/gen/out.rs index 2a5b17d..8783dc2 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -1,8 +1,9 @@ use crate::gen::include::Includes; +use crate::gen::namespace::Namespace; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { - pub namespace: Vec, + pub namespace: Namespace, pub header: bool, pub include: Includes, content: Vec, @@ -11,7 +12,7 @@ pub(crate) struct OutFile { } impl OutFile { - pub fn new(namespace: Vec, header: bool) -> Self { + pub fn new(namespace: Namespace, header: bool) -> Self { OutFile { namespace, header, diff --git a/gen/write.rs b/gen/write.rs index a17f8f7..a1d5748 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,3 +1,4 @@ +use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -5,7 +6,7 @@ use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var}; use proc_macro2::Ident; pub(super) fn gen( - namespace: Vec, + namespace: Namespace, apis: &[Api], types: &Types, opt: Opt, From 7ece56fb099f63c480ca58f004fda69aad3e82d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 04:31:26 +0000 Subject: [PATCH 205/2232] Allow write_fmt to work on &OutFile --- diff --git a/gen/mod.rs b/gen/mod.rs index 8006898..523ba1d 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -9,7 +9,6 @@ mod write; use self::error::format_err; use self::namespace::Namespace; -use self::out::OutFile; use crate::syntax::{self, check, ident, Types}; use quote::quote; use std::fs; @@ -44,17 +43,17 @@ pub(super) struct Opt { pub include: Vec, } -pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> OutFile { +pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; generate(path, opt, header) } -pub(super) fn do_generate_header(path: &Path, opt: Opt) -> OutFile { +pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { let header = true; generate(path, opt, header) } -fn generate(path: &Path, opt: Opt, header: bool) -> OutFile { +fn generate(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), @@ -68,7 +67,7 @@ fn generate(path: &Path, opt: Opt, header: bool) -> OutFile { let out = write::gen(bridge.namespace, &apis, &types, opt, header); Ok(out) })() { - Ok(out) => out, + Ok(out) => out.content(), Err(err) => format_err(path, &source, err), } } diff --git a/gen/out.rs b/gen/out.rs index 8783dc2..35f2cd7 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -1,12 +1,17 @@ use crate::gen::include::Includes; use crate::gen::namespace::Namespace; +use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { pub namespace: Namespace, pub header: bool, pub include: Includes, - content: Vec, + content: RefCell, +} + +struct Content { + bytes: Vec, section_pending: bool, blocks_pending: Vec<&'static str>, } @@ -17,65 +22,70 @@ impl OutFile { namespace, header, include: Includes::new(), - content: Vec::new(), - section_pending: false, - blocks_pending: Vec::new(), + content: RefCell::new(Content { + bytes: Vec::new(), + section_pending: false, + blocks_pending: Vec::new(), + }), } } // Write a blank line if the preceding section had any contents. pub fn next_section(&mut self) { - self.section_pending = true; + let content = self.content.get_mut(); + content.section_pending = true; } pub fn begin_block(&mut self, block: &'static str) { - self.blocks_pending.push(block); + let content = self.content.get_mut(); + content.blocks_pending.push(block); } pub fn end_block(&mut self, block: &'static str) { - if self.blocks_pending.pop().is_none() { - self.content.extend_from_slice(b"} // "); - self.content.extend_from_slice(block.as_bytes()); - self.content.push(b'\n'); - self.section_pending = true; + let content = self.content.get_mut(); + if content.blocks_pending.pop().is_none() { + content.bytes.extend_from_slice(b"} // "); + content.bytes.extend_from_slice(block.as_bytes()); + content.bytes.push(b'\n'); + content.section_pending = true; } } pub fn prepend(&mut self, section: String) { - self.content.splice(..0, section.into_bytes()); + let content = self.content.get_mut(); + content.bytes.splice(..0, section.into_bytes()); + } + + pub fn write_fmt(&self, args: Arguments) { + let content = &mut *self.content.borrow_mut(); + Write::write_fmt(content, args).unwrap(); } - pub fn write_fmt(&mut self, args: Arguments) { - Write::write_fmt(self, args).unwrap(); + pub fn content(&self) -> Vec { + self.content.borrow().bytes.clone() } } -impl Write for OutFile { +impl Write for Content { fn write_str(&mut self, s: &str) -> fmt::Result { if !s.is_empty() { if !self.blocks_pending.is_empty() { - if !self.content.is_empty() { - self.content.push(b'\n'); + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); } for block in self.blocks_pending.drain(..) { - self.content.extend_from_slice(block.as_bytes()); - self.content.extend_from_slice(b" {\n"); + self.bytes.extend_from_slice(block.as_bytes()); + self.bytes.extend_from_slice(b" {\n"); } self.section_pending = false; } else if self.section_pending { - if !self.content.is_empty() { - self.content.push(b'\n'); + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); } self.section_pending = false; } - self.content.extend_from_slice(s.as_bytes()); + self.bytes.extend_from_slice(s.as_bytes()); } Ok(()) } } - -impl AsRef<[u8]> for OutFile { - fn as_ref(&self) -> &[u8] { - &self.content - } -} From d815de0195c9af754542052bb29bb9659decda6b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 04:31:26 +0000 Subject: [PATCH 206/2232] Levarage the Display impl for namespace printing --- diff --git a/gen/write.rs b/gen/write.rs index a1d5748..1f7adda 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -305,10 +305,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - for name in out.namespace.clone() { - write!(out, "{}$", name); - } - write!(out, "cxxbridge02${}(", efn.ident); + write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -413,10 +410,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - for name in out.namespace.clone() { - write!(out, "{}$", name); - } - write!(out, "cxxbridge02${}(", efn.ident); + write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -487,10 +481,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if efn.throws { write!(out, "::rust::Str::Repr error$ = "); } - for name in out.namespace.clone() { - write!(out, "{}$", name); - } - write!(out, "cxxbridge02${}(", efn.ident); + write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); From d71eb54c139b5dd576efe778751dda5c94a399f8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 04:31:26 +0000 Subject: [PATCH 207/2232] No longer need to iterate Namespace by value --- diff --git a/gen/namespace.rs b/gen/namespace.rs index c644352..557e331 100644 --- a/gen/namespace.rs +++ b/gen/namespace.rs @@ -1,6 +1,5 @@ use std::fmt::{self, Display}; use std::slice::Iter; -use std::vec::IntoIter; #[derive(Clone)] pub struct Namespace { @@ -34,11 +33,3 @@ impl<'a> IntoIterator for &'a Namespace { self.iter() } } - -impl IntoIterator for Namespace { - type Item = String; - type IntoIter = IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.segments.into_iter() - } -} From 75dca2e84620d79b8a69155d10967761ea543cdf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 04:50:25 +0000 Subject: [PATCH 208/2232] Passing function pointer from Rust to C++ --- diff --git a/gen/write.rs b/gen/write.rs index 1f7adda..b745739 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,7 @@ use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var}; +use crate::syntax::{Api, ExternFn, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; pub(super) fn gen( @@ -109,6 +109,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_string = false; let mut needs_rust_str = false; let mut needs_rust_box = false; + let mut needs_rust_fn = false; for ty in types { match ty { Type::RustBox(_) => { @@ -120,6 +121,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.string = true; needs_rust_str = true; } + Type::Fn(_) => { + needs_rust_fn = true; + } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -175,6 +179,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_rust_string || needs_rust_str || needs_rust_box + || needs_rust_fn || needs_rust_error || needs_unsafe_bitcopy || needs_manually_drop @@ -192,6 +197,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); @@ -402,27 +408,71 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, " return throw$;"); } writeln!(out, "}}"); + for arg in &efn.args { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + write_function_pointer_trampoline(out, efn, var, f, types); + } + } +} + +fn write_function_pointer_trampoline( + out: &mut OutFile, + efn: &ExternFn, + var: &Ident, + f: &Signature, + types: &Types, +) { + out.next_section(); + let r_trampoline = format!("{}cxxbridge02${}${}$1", out.namespace, efn.ident, var); + let indirect_call = true; + write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); + + out.next_section(); + let c_trampoline = format!("{}cxxbridge02${}${}$0", out.namespace, efn.ident, var); + write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - if efn.throws { + let link_name = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let indirect_call = false; + write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); +} + +fn write_rust_function_decl_impl( + out: &mut OutFile, + link_name: &str, + sig: &Signature, + types: &Types, + indirect_call: bool, +) { + if sig.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type_space(out, &efn.ret, types); + write_extern_return_type_space(out, &sig.ret, types); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { + write!(out, "{}(", link_name); + let mut needs_comma = false; + for arg in &sig.args { + if needs_comma { write!(out, ", "); } write_extern_arg(out, arg, types); + needs_comma = true; } - if indirect_return(efn, types) { - if !efn.args.is_empty() { + if indirect_return(sig, types) { + if needs_comma { write!(out, ", "); } - write_return_type(out, &efn.ret); + write_return_type(out, &sig.ret); write!(out, "*return$"); + needs_comma = true; + } + if indirect_call { + if needs_comma { + write!(out, ", "); + } + write!(out, "void *"); } writeln!(out, ") noexcept;"); } @@ -431,24 +481,44 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } - write_return_type(out, &efn.ret); - write!(out, "{}(", efn.ident); - for (i, arg) in efn.args.iter().enumerate() { + let local_name = efn.ident.to_string(); + let invoke = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let indirect_call = false; + write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &str, + indirect_call: bool, +) { + write_return_type(out, &sig.ret); + write!(out, "{}(", local_name); + for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type_space(out, &arg.ty); write!(out, "{}", arg.ident); } + if indirect_call { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "void *extern$"); + } write!(out, ")"); - if !efn.throws { + if !sig.throws { write!(out, " noexcept"); } if out.header { writeln!(out, ";"); } else { writeln!(out, " {{"); - for arg in &efn.args { + for arg in &sig.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); @@ -457,13 +527,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } write!(out, " "); - let indirect_return = indirect_return(efn, types); + let indirect_return = indirect_return(sig, types); if indirect_return { write!(out, "::rust::MaybeUninit<"); - write_type(out, efn.ret.as_ref().unwrap()); + write_type(out, sig.ret.as_ref().unwrap()); writeln!(out, "> return$;"); write!(out, " "); - } else if let Some(ret) = &efn.ret { + } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { @@ -478,11 +548,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { _ => {} } } - if efn.throws { + if sig.throws { write!(out, "::rust::Str::Repr error$ = "); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); - for (i, arg) in efn.args.iter().enumerate() { + write!(out, "{}(", invoke); + for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } @@ -501,19 +571,25 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } if indirect_return { - if !efn.args.is_empty() { + if !sig.args.is_empty() { write!(out, ", "); } write!(out, "&return$.value"); } + if indirect_call { + if !sig.args.is_empty() || indirect_return { + write!(out, ", "); + } + write!(out, "extern$"); + } write!(out, ")"); - if let Some(ret) = &efn.ret { + if let Some(ret) = &sig.ret { if let Type::RustBox(_) | Type::UniquePtr(_) = ret { write!(out, ")"); } } writeln!(out, ";"); - if efn.throws { + if sig.throws { writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::Error(error$);"); writeln!(out, " }}"); @@ -533,10 +609,10 @@ fn write_return_type(out: &mut OutFile, ty: &Option) { } } -fn indirect_return(efn: &ExternFn, types: &Types) -> bool { - efn.ret +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret .as_ref() - .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { @@ -640,7 +716,21 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } - Type::Fn(_) => unimplemented!(), + Type::Fn(f) => { + write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); + match &f.ret { + Some(ret) => write_type(out, ret), + None => write!(out, "void"), + } + write!(out, "("); + for (i, arg) in f.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")>"); + } Type::Void(_) => unreachable!(), } } @@ -652,9 +742,10 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), + Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::Fn(_) => { + write!(out, " ") + } Type::Ref(_) => {} - Type::Fn(_) => unimplemented!(), Type::Void(_) => unreachable!(), } } diff --git a/include/cxx.h b/include/cxx.h index 50f8f69..3dd52e5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -147,6 +147,23 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE02_RUST_FN +#define CXXBRIDGE02_RUST_FN +template class Fn; + +template +class Fn { +public: + Ret operator()(Args... args) noexcept(!Throws); + +private: + Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); + void *fn; +}; + +template using TryFn = Fn; +#endif // CXXBRIDGE02_RUST_FN + #ifndef CXXBRIDGE02_RUST_ERROR #define CXXBRIDGE02_RUST_ERROR class Error final : std::exception { @@ -170,6 +187,9 @@ using string = String; using str = Str; template using box = Box; using error = Error; +template +using fn = Fn; +template using try_fn = TryFn; #ifndef CXXBRIDGE02_RUST_BITCOPY #define CXXBRIDGE02_RUST_BITCOPY @@ -179,5 +199,10 @@ struct unsafe_bitcopy_t { constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE02_RUST_BITCOPY +template +Ret Fn::operator()(Args... args) noexcept(!Throws) { + return (*this->trampoline)(std::move(args)..., this->fn); +} + } // namespace cxxbridge02 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 69b7fb5..6afb548 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,9 +1,9 @@ use crate::namespace::Namespace; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{self, check, Api, ExternFn, ExternType, Struct, Type, Types}; +use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::{Error, ItemMod, Result, Token}; +use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -128,6 +128,8 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ty = expand_extern_type(&arg.ty); if arg.ty == RustString { quote!(#ident: *const #ty) + } else if let Type::Fn(_) = arg.ty { + quote!(#ident: ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { quote!(#ident: *mut #ty) } else { @@ -186,6 +188,20 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), } }); + let trampolines = efn + .args + .iter() + .filter_map(|arg| { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + Some(expand_function_pointer_trampoline( + namespace, efn, var, f, types, + )) + } else { + None + } + }) + .collect::(); let mut setup = efn .args .iter() @@ -261,6 +277,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types extern "C" { #decl } + #trampolines unsafe { #setup #expr @@ -269,6 +286,41 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } } +fn expand_function_pointer_trampoline( + namespace: &Namespace, + efn: &ExternFn, + var: &Ident, + sig: &Signature, + types: &Types, +) -> TokenStream { + let c_trampoline = format!("{}cxxbridge02${}${}$0", namespace, efn.ident, var); + let r_trampoline = format!("{}cxxbridge02${}${}$1", namespace, efn.ident, var); + let local_name = parse_quote!(__); + let catch_unwind_label = format!("::{}::{}", efn.ident, var); + let shim = expand_rust_function_shim_impl( + sig, + types, + &r_trampoline, + local_name, + catch_unwind_label, + None, + ); + + quote! { + let #var = ::cxx::private::FatFunction { + trampoline: { + extern "C" { + #[link_name = #c_trampoline] + fn trampoline(); + } + #shim + trampoline as usize as *const () + }, + ptr: #var as usize as *const (), + }; + } +} + fn expand_rust_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; quote! { @@ -278,7 +330,29 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let args = efn.args.iter().map(|arg| { + let link_name = format!("{}cxxbridge02${}", namespace, ident); + let local_name = format_ident!("__{}", ident); + let catch_unwind_label = format!("::{}", ident); + let invoke = Some(ident); + expand_rust_function_shim_impl( + efn, + types, + &link_name, + local_name, + catch_unwind_label, + invoke, + ) +} + +fn expand_rust_function_shim_impl( + sig: &Signature, + types: &Types, + link_name: &str, + local_name: Ident, + catch_unwind_label: String, + invoke: Option<&Ident>, +) -> TokenStream { + let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); if types.needs_indirect_abi(&arg.ty) { @@ -287,7 +361,8 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type quote!(#ident: #ty) } }); - let vars = efn.args.iter().map(|arg| { + + let vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { Type::Ident(i) if i == RustString => { @@ -304,9 +379,14 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => quote!(#ident), } }); - let mut outparam = None; - let call = quote!(super::#ident(#(#vars),*)); - let mut expr = efn + + let mut call = match invoke { + Some(ident) => quote!(super::#ident), + None => quote!(__extern), + }; + call.extend(quote! { (#(#vars),*) }); + + let mut expr = sig .ret .as_ref() .and_then(|ret| match ret { @@ -325,13 +405,15 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => None, }) .unwrap_or(call); - let indirect_return = indirect_return(efn, types); + + let mut outparam = None; + let indirect_return = indirect_return(sig, types); if indirect_return { - let ret = expand_extern_type(efn.ret.as_ref().unwrap()); - outparam = Some(quote!(__return: *mut #ret)); + let ret = expand_extern_type(sig.ret.as_ref().unwrap()); + outparam = Some(quote!(__return: *mut #ret,)); } - if efn.throws { - let out = match efn.ret { + if sig.throws { + let out = match sig.ret { Some(_) => quote!(__return), None => quote!(&mut ()), }; @@ -339,19 +421,24 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } else if indirect_return { expr = quote!(::std::ptr::write(__return, #expr)); } + expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); - let ret = if efn.throws { + + let ret = if sig.throws { quote!(-> ::cxx::private::Result) } else { - expand_extern_return_type(&efn.ret, types) + expand_extern_return_type(&sig.ret, types) }; - let link_name = format!("{}cxxbridge02${}", namespace, ident); - let local_name = format_ident!("__{}", ident); - let catch_unwind_label = format!("::{}", ident); + + let pointer = match invoke { + None => Some(quote!(__extern: #sig)), + Some(_) => None, + }; + quote! { #[doc(hidden)] #[export_name = #link_name] - unsafe extern "C" fn #local_name(#(#args,)* #outparam) #ret { + unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret { let __fn = concat!(module_path!(), #catch_unwind_label); #expr } @@ -457,10 +544,10 @@ fn expand_return_type(ret: &Option) -> TokenStream { } } -fn indirect_return(efn: &ExternFn, types: &Types) -> bool { - efn.ret +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret .as_ref() - .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } fn expand_extern_type(ty: &Type) -> TokenStream { diff --git a/src/function.rs b/src/function.rs new file mode 100644 index 0000000..1166b3d --- /dev/null +++ b/src/function.rs @@ -0,0 +1,5 @@ +#[repr(C)] +pub struct FatFunction { + pub trampoline: *const (), + pub ptr: *const (), +} diff --git a/src/lib.rs b/src/lib.rs index 09326f5..3aaf172 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,6 +356,7 @@ mod assert; mod cxx_string; mod error; mod exception; +mod function; mod gen; mod opaque; mod paths; @@ -374,6 +375,7 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; pub use crate::rust_str::RustStr; diff --git a/syntax/check.rs b/syntax/check.rs index b44d0cc..2826a85 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types}; +use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Struct, Ty1, Type, Types}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; @@ -136,10 +136,12 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { cx.error(arg, msg); } if let Type::Fn(_) = arg.ty { - cx.error( - arg, - "passing a function pointer argument is not implemented yet", - ); + if efn.lang == Lang::Rust { + cx.error( + arg, + "passing a function pointer from C++ to Rust is not implemented yet", + ); + } } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 492fc91..7df0110 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -32,6 +32,7 @@ pub mod ffi { fn c_take_str(s: &str); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 6384ac1..4aad3a2 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -92,6 +92,10 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_take_callback(rust::Fn callback) { + callback("2020"); +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index e0e7624..6713614 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -35,6 +35,7 @@ void c_take_ref_c(const C &c); void c_take_str(rust::Str s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_take_callback(rust::Fn callback); void c_try_return_void(); size_t c_try_return_primitive(); diff --git a/tests/test.rs b/tests/test.rs index 6a6fc3e..f8e9ca8 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -78,6 +78,18 @@ fn test_c_take() { } #[test] +fn test_c_callback() { + fn callback(s: String) -> usize { + if s == "2020" { + cxx_test_suite_set_correct(); + } + 0 + } + + check!(ffi::c_take_callback(callback)); +} + +#[test] fn test_c_call_r() { fn cxx_run_test() { extern "C" { From 6c39077541e26e93ed13402839c71e844aae26d3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 05:19:15 +0000 Subject: [PATCH 209/2232] Merge pull request #85 from dtolnay/fn Implement function pointer passing from Rust to C++ --- diff --git a/gen/write.rs b/gen/write.rs index 1f7adda..b745739 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,7 @@ use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var}; +use crate::syntax::{Api, ExternFn, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; pub(super) fn gen( @@ -109,6 +109,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_string = false; let mut needs_rust_str = false; let mut needs_rust_box = false; + let mut needs_rust_fn = false; for ty in types { match ty { Type::RustBox(_) => { @@ -120,6 +121,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.string = true; needs_rust_str = true; } + Type::Fn(_) => { + needs_rust_fn = true; + } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -175,6 +179,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_rust_string || needs_rust_str || needs_rust_box + || needs_rust_fn || needs_rust_error || needs_unsafe_bitcopy || needs_manually_drop @@ -192,6 +197,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); @@ -402,27 +408,71 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, " return throw$;"); } writeln!(out, "}}"); + for arg in &efn.args { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + write_function_pointer_trampoline(out, efn, var, f, types); + } + } +} + +fn write_function_pointer_trampoline( + out: &mut OutFile, + efn: &ExternFn, + var: &Ident, + f: &Signature, + types: &Types, +) { + out.next_section(); + let r_trampoline = format!("{}cxxbridge02${}${}$1", out.namespace, efn.ident, var); + let indirect_call = true; + write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); + + out.next_section(); + let c_trampoline = format!("{}cxxbridge02${}${}$0", out.namespace, efn.ident, var); + write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - if efn.throws { + let link_name = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let indirect_call = false; + write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); +} + +fn write_rust_function_decl_impl( + out: &mut OutFile, + link_name: &str, + sig: &Signature, + types: &Types, + indirect_call: bool, +) { + if sig.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type_space(out, &efn.ret, types); + write_extern_return_type_space(out, &sig.ret, types); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { + write!(out, "{}(", link_name); + let mut needs_comma = false; + for arg in &sig.args { + if needs_comma { write!(out, ", "); } write_extern_arg(out, arg, types); + needs_comma = true; } - if indirect_return(efn, types) { - if !efn.args.is_empty() { + if indirect_return(sig, types) { + if needs_comma { write!(out, ", "); } - write_return_type(out, &efn.ret); + write_return_type(out, &sig.ret); write!(out, "*return$"); + needs_comma = true; + } + if indirect_call { + if needs_comma { + write!(out, ", "); + } + write!(out, "void *"); } writeln!(out, ") noexcept;"); } @@ -431,24 +481,44 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } - write_return_type(out, &efn.ret); - write!(out, "{}(", efn.ident); - for (i, arg) in efn.args.iter().enumerate() { + let local_name = efn.ident.to_string(); + let invoke = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let indirect_call = false; + write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &str, + indirect_call: bool, +) { + write_return_type(out, &sig.ret); + write!(out, "{}(", local_name); + for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type_space(out, &arg.ty); write!(out, "{}", arg.ident); } + if indirect_call { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "void *extern$"); + } write!(out, ")"); - if !efn.throws { + if !sig.throws { write!(out, " noexcept"); } if out.header { writeln!(out, ";"); } else { writeln!(out, " {{"); - for arg in &efn.args { + for arg in &sig.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); @@ -457,13 +527,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } write!(out, " "); - let indirect_return = indirect_return(efn, types); + let indirect_return = indirect_return(sig, types); if indirect_return { write!(out, "::rust::MaybeUninit<"); - write_type(out, efn.ret.as_ref().unwrap()); + write_type(out, sig.ret.as_ref().unwrap()); writeln!(out, "> return$;"); write!(out, " "); - } else if let Some(ret) = &efn.ret { + } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { @@ -478,11 +548,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { _ => {} } } - if efn.throws { + if sig.throws { write!(out, "::rust::Str::Repr error$ = "); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); - for (i, arg) in efn.args.iter().enumerate() { + write!(out, "{}(", invoke); + for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } @@ -501,19 +571,25 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } } if indirect_return { - if !efn.args.is_empty() { + if !sig.args.is_empty() { write!(out, ", "); } write!(out, "&return$.value"); } + if indirect_call { + if !sig.args.is_empty() || indirect_return { + write!(out, ", "); + } + write!(out, "extern$"); + } write!(out, ")"); - if let Some(ret) = &efn.ret { + if let Some(ret) = &sig.ret { if let Type::RustBox(_) | Type::UniquePtr(_) = ret { write!(out, ")"); } } writeln!(out, ";"); - if efn.throws { + if sig.throws { writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::Error(error$);"); writeln!(out, " }}"); @@ -533,10 +609,10 @@ fn write_return_type(out: &mut OutFile, ty: &Option) { } } -fn indirect_return(efn: &ExternFn, types: &Types) -> bool { - efn.ret +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret .as_ref() - .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { @@ -640,7 +716,21 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Str(_) => { write!(out, "::rust::Str"); } - Type::Fn(_) => unimplemented!(), + Type::Fn(f) => { + write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); + match &f.ret { + Some(ret) => write_type(out, ret), + None => write!(out, "void"), + } + write!(out, "("); + for (i, arg) in f.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")>"); + } Type::Void(_) => unreachable!(), } } @@ -652,9 +742,10 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "), + Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::Fn(_) => { + write!(out, " ") + } Type::Ref(_) => {} - Type::Fn(_) => unimplemented!(), Type::Void(_) => unreachable!(), } } diff --git a/include/cxx.h b/include/cxx.h index 50f8f69..3dd52e5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -147,6 +147,23 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE02_RUST_FN +#define CXXBRIDGE02_RUST_FN +template class Fn; + +template +class Fn { +public: + Ret operator()(Args... args) noexcept(!Throws); + +private: + Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); + void *fn; +}; + +template using TryFn = Fn; +#endif // CXXBRIDGE02_RUST_FN + #ifndef CXXBRIDGE02_RUST_ERROR #define CXXBRIDGE02_RUST_ERROR class Error final : std::exception { @@ -170,6 +187,9 @@ using string = String; using str = Str; template using box = Box; using error = Error; +template +using fn = Fn; +template using try_fn = TryFn; #ifndef CXXBRIDGE02_RUST_BITCOPY #define CXXBRIDGE02_RUST_BITCOPY @@ -179,5 +199,10 @@ struct unsafe_bitcopy_t { constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE02_RUST_BITCOPY +template +Ret Fn::operator()(Args... args) noexcept(!Throws) { + return (*this->trampoline)(std::move(args)..., this->fn); +} + } // namespace cxxbridge02 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 69b7fb5..6afb548 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,9 +1,9 @@ use crate::namespace::Namespace; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{self, check, Api, ExternFn, ExternType, Struct, Type, Types}; +use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::{Error, ItemMod, Result, Token}; +use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -128,6 +128,8 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ty = expand_extern_type(&arg.ty); if arg.ty == RustString { quote!(#ident: *const #ty) + } else if let Type::Fn(_) = arg.ty { + quote!(#ident: ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { quote!(#ident: *mut #ty) } else { @@ -186,6 +188,20 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), } }); + let trampolines = efn + .args + .iter() + .filter_map(|arg| { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + Some(expand_function_pointer_trampoline( + namespace, efn, var, f, types, + )) + } else { + None + } + }) + .collect::(); let mut setup = efn .args .iter() @@ -261,6 +277,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types extern "C" { #decl } + #trampolines unsafe { #setup #expr @@ -269,6 +286,41 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } } +fn expand_function_pointer_trampoline( + namespace: &Namespace, + efn: &ExternFn, + var: &Ident, + sig: &Signature, + types: &Types, +) -> TokenStream { + let c_trampoline = format!("{}cxxbridge02${}${}$0", namespace, efn.ident, var); + let r_trampoline = format!("{}cxxbridge02${}${}$1", namespace, efn.ident, var); + let local_name = parse_quote!(__); + let catch_unwind_label = format!("::{}::{}", efn.ident, var); + let shim = expand_rust_function_shim_impl( + sig, + types, + &r_trampoline, + local_name, + catch_unwind_label, + None, + ); + + quote! { + let #var = ::cxx::private::FatFunction { + trampoline: { + extern "C" { + #[link_name = #c_trampoline] + fn trampoline(); + } + #shim + trampoline as usize as *const () + }, + ptr: #var as usize as *const (), + }; + } +} + fn expand_rust_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; quote! { @@ -278,7 +330,29 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let args = efn.args.iter().map(|arg| { + let link_name = format!("{}cxxbridge02${}", namespace, ident); + let local_name = format_ident!("__{}", ident); + let catch_unwind_label = format!("::{}", ident); + let invoke = Some(ident); + expand_rust_function_shim_impl( + efn, + types, + &link_name, + local_name, + catch_unwind_label, + invoke, + ) +} + +fn expand_rust_function_shim_impl( + sig: &Signature, + types: &Types, + link_name: &str, + local_name: Ident, + catch_unwind_label: String, + invoke: Option<&Ident>, +) -> TokenStream { + let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); if types.needs_indirect_abi(&arg.ty) { @@ -287,7 +361,8 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type quote!(#ident: #ty) } }); - let vars = efn.args.iter().map(|arg| { + + let vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { Type::Ident(i) if i == RustString => { @@ -304,9 +379,14 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => quote!(#ident), } }); - let mut outparam = None; - let call = quote!(super::#ident(#(#vars),*)); - let mut expr = efn + + let mut call = match invoke { + Some(ident) => quote!(super::#ident), + None => quote!(__extern), + }; + call.extend(quote! { (#(#vars),*) }); + + let mut expr = sig .ret .as_ref() .and_then(|ret| match ret { @@ -325,13 +405,15 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type _ => None, }) .unwrap_or(call); - let indirect_return = indirect_return(efn, types); + + let mut outparam = None; + let indirect_return = indirect_return(sig, types); if indirect_return { - let ret = expand_extern_type(efn.ret.as_ref().unwrap()); - outparam = Some(quote!(__return: *mut #ret)); + let ret = expand_extern_type(sig.ret.as_ref().unwrap()); + outparam = Some(quote!(__return: *mut #ret,)); } - if efn.throws { - let out = match efn.ret { + if sig.throws { + let out = match sig.ret { Some(_) => quote!(__return), None => quote!(&mut ()), }; @@ -339,19 +421,24 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type } else if indirect_return { expr = quote!(::std::ptr::write(__return, #expr)); } + expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); - let ret = if efn.throws { + + let ret = if sig.throws { quote!(-> ::cxx::private::Result) } else { - expand_extern_return_type(&efn.ret, types) + expand_extern_return_type(&sig.ret, types) }; - let link_name = format!("{}cxxbridge02${}", namespace, ident); - let local_name = format_ident!("__{}", ident); - let catch_unwind_label = format!("::{}", ident); + + let pointer = match invoke { + None => Some(quote!(__extern: #sig)), + Some(_) => None, + }; + quote! { #[doc(hidden)] #[export_name = #link_name] - unsafe extern "C" fn #local_name(#(#args,)* #outparam) #ret { + unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret { let __fn = concat!(module_path!(), #catch_unwind_label); #expr } @@ -457,10 +544,10 @@ fn expand_return_type(ret: &Option) -> TokenStream { } } -fn indirect_return(efn: &ExternFn, types: &Types) -> bool { - efn.ret +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret .as_ref() - .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret)) + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } fn expand_extern_type(ty: &Type) -> TokenStream { diff --git a/src/function.rs b/src/function.rs new file mode 100644 index 0000000..1166b3d --- /dev/null +++ b/src/function.rs @@ -0,0 +1,5 @@ +#[repr(C)] +pub struct FatFunction { + pub trampoline: *const (), + pub ptr: *const (), +} diff --git a/src/lib.rs b/src/lib.rs index 09326f5..3aaf172 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,6 +356,7 @@ mod assert; mod cxx_string; mod error; mod exception; +mod function; mod gen; mod opaque; mod paths; @@ -374,6 +375,7 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; pub use crate::rust_str::RustStr; diff --git a/syntax/check.rs b/syntax/check.rs index b44d0cc..2826a85 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types}; +use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Struct, Ty1, Type, Types}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; @@ -136,10 +136,12 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { cx.error(arg, msg); } if let Type::Fn(_) = arg.ty { - cx.error( - arg, - "passing a function pointer argument is not implemented yet", - ); + if efn.lang == Lang::Rust { + cx.error( + arg, + "passing a function pointer from C++ to Rust is not implemented yet", + ); + } } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 492fc91..7df0110 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -32,6 +32,7 @@ pub mod ffi { fn c_take_str(s: &str); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 6384ac1..4aad3a2 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -92,6 +92,10 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_take_callback(rust::Fn callback) { + callback("2020"); +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index e0e7624..6713614 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -35,6 +35,7 @@ void c_take_ref_c(const C &c); void c_take_str(rust::Str s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_take_callback(rust::Fn callback); void c_try_return_void(); size_t c_try_return_primitive(); diff --git a/tests/test.rs b/tests/test.rs index 6a6fc3e..f8e9ca8 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -78,6 +78,18 @@ fn test_c_take() { } #[test] +fn test_c_callback() { + fn callback(s: String) -> usize { + if s == "2020" { + cxx_test_suite_set_correct(); + } + 0 + } + + check!(ffi::c_take_callback(callback)); +} + +#[test] fn test_c_call_r() { fn cxx_run_test() { extern "C" { From addc748bbcfc4d3837994db9f3ffb53e25bc270b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 05:19:48 +0000 Subject: [PATCH 210/2232] Add function pointers to builtin types table --- diff --git a/README.md b/README.md index 4308291..fee7299 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ returns of functions. CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far Result<T>error <=> exceptionallowed as return type only diff --git a/src/lib.rs b/src/lib.rs index 3aaf172..8a80553 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -306,6 +306,7 @@ //! CxxStringstd::stringcannot be passed by value //! Box<T>rust::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +//! fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far //! Result<T>error <=> exceptionallowed as return type only //! //! From 864ab8c6e277412574e5f4e6e1c13a60b1e69359 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 30 2020 05:25:40 +0000 Subject: [PATCH 211/2232] Add github repo link to documentation --- diff --git a/src/lib.rs b/src/lib.rs index 8a80553..dfd3414 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +//! **[https://github.com/dtolnay/cxx]** +//! +//!
+//! //! This library provides a **safe** mechanism for calling C++ code from Rust //! and Rust code from C++, not subject to the many ways that things can go //! wrong when using bindgen or cbindgen to generate unsafe C-style bindings. From 7261732ba9c7e41e5e8758eb5e34d672bf3d5e7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 31 2020 02:45:41 +0000 Subject: [PATCH 212/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 14f2542..44c3a6a 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -2,7 +2,7 @@ rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.27/src/**"]), + srcs = glob(["vendor/anyhow-1.0.28/src/**"]), visibility = ["PUBLIC"], features = ["std"], ) @@ -31,7 +31,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.2/src/**"]), visibility = ["PUBLIC"], deps = [ ":termcolor", @@ -59,7 +59,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.11/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.12/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -71,7 +71,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.11/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.12/src/**"]), proc_macro = True, deps = [ ":proc-macro2", @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.9/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.16/src/**"]), + srcs = glob(["vendor/syn-1.0.17/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", @@ -171,14 +171,14 @@ rust_library( rust_library( name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + srcs = glob(["vendor/thiserror-1.0.14/src/**"]), visibility = ["PUBLIC"], deps = [":thiserror-impl"], ) rust_library( name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + srcs = glob(["vendor/thiserror-impl-1.0.14/src/**"]), proc_macro = True, deps = [ ":proc-macro2", diff --git a/third-party/BUILD b/third-party/BUILD index daf9ea1..646289c 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -7,7 +7,7 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.27/src/**"]), + srcs = glob(["vendor/anyhow-1.0.28/src/**"]), crate_features = ["std"], visibility = ["//visibility:public"], ) @@ -36,7 +36,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.0/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.2/src/**"]), visibility = ["//visibility:public"], deps = [ ":termcolor", @@ -64,7 +64,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.11/src/**"]), + srcs = glob(["vendor/proc-macro-error-0.4.12/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -76,7 +76,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.11/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-0.4.12/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.9/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.16/src/**"]), + srcs = glob(["vendor/syn-1.0.17/src/**"]), crate_features = [ "clone-impls", "derive", @@ -176,14 +176,14 @@ rust_library( rust_library( name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.11/src/**"]), + srcs = glob(["vendor/thiserror-1.0.14/src/**"]), visibility = ["//visibility:public"], deps = [":thiserror-impl"], ) rust_library( name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.11/src/**"]), + srcs = glob(["vendor/thiserror-impl-1.0.14/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index e76c831..5c821c5 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" +checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" [[package]] name = "atty" @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7606d610349258b637bb639f7565bb7ee5fd6114130d2af59d0f39154e92426a" +checksum = "4efca5ddfdf45cee2eedd9dadbe7a5fa90a5536af6f580f1814267b2a4d6107f" dependencies = [ "termcolor", "unicode-width", @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" dependencies = [ "proc-macro2", "quote", @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ "unicode-xid", ] @@ -252,9 +252,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" dependencies = [ "itoa", "ryu", @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", "quote", @@ -333,18 +333,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +checksum = "f0570dc61221295909abdb95c739f2e74325e14293b2026b0a7e195091ec54ae" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +checksum = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd" dependencies = [ "proc-macro2", "quote", @@ -422,9 +422,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" dependencies = [ "winapi", ] From d4402cae9beeaafe3b45e2885fd97d2f99e58d3c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mar 31 2020 02:47:58 +0000 Subject: [PATCH 213/2232] Release 0.2.1 --- diff --git a/Cargo.toml b/Cargo.toml index 63a3a94..2cc6fff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.0" # remember to update html_root_url +version = "0.2.1" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.0", path = "macro" } +cxxbridge-macro = { version = "=0.2.1", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index aef31f6..d100adc 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.0" +version = "0.2.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 635d7cc..9456cda 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.0" +version = "0.2.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index dfd3414..28f113a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.1.2")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.1")] #![deny(improper_ctypes)] #![allow( clippy::declare_interior_mutable_const, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 5c821c5..59dbbb7 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.0" +version = "0.2.1" dependencies = [ "cxx", "proc-macro2", From e0bad9300c49f5bb631e256135feac326c9f8517 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 02 2020 21:16:29 +0000 Subject: [PATCH 214/2232] Remove finished item from todo list --- diff --git a/README.md b/README.md index fee7299..b5c57fd 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,6 @@ the facets that I still intend for this project to tackle: - [ ] Support associated methods: `extern "Rust" { fn f(self: &Struct); }` - [ ] Support C++ member functions -- [ ] Support passing function pointers across the FFI - [ ] Support structs with type parameters - [ ] Support async functions From 737e02eeb197c256c8dc8062fbec2f2a19c2f035 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 05 2020 04:52:46 +0000 Subject: [PATCH 215/2232] Remove unneeded return from void functions --- diff --git a/gen/write.rs b/gen/write.rs index b745739..3d1fdf3 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -821,12 +821,12 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " return cxxbridge02$box${}$uninit(this);", instance); + writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " return cxxbridge02$box${}$drop(this);", instance); + writeln!(out, " cxxbridge02$box${}$drop(this);", instance); writeln!(out, "}}"); } From e3a481508acf69504b046cb4f9eee1543ddeb0e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:01:16 +0000 Subject: [PATCH 216/2232] Store finer grained tokens of Signature This is required in order for function pointers like `fn(&CxxString)` to work, which requires the cxx bridge to emit `fn(&::cxx::CxxString)` rather than a straight copy of the input tokens. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6afb548..1bb3628 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -273,7 +273,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .unwrap_or(call); quote! { #doc - pub fn #ident(#(#args),*) #ret { + pub fn #ident(#args) #ret { extern "C" { #decl } diff --git a/syntax/impls.rs b/syntax/impls.rs index 27aa43a..34fb851 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -116,7 +116,8 @@ impl PartialEq for Signature { args, ret, throws, - tokens: _, + paren_token: _, + throws_tokens: _, } = self; let Signature { fn_token: _, @@ -124,9 +125,14 @@ impl PartialEq for Signature { args: args2, ret: ret2, throws: throws2, - tokens: _, + paren_token: _, + throws_tokens: _, } = other; - receiver == receiver2 && args == args2 && ret == ret2 && throws == throws2 + receiver == receiver2 + && ret == ret2 + && throws == throws2 + && args.len() == args2.len() + && args.iter().zip(args2).all(|(arg, arg2)| arg == arg2) } } @@ -138,10 +144,13 @@ impl Hash for Signature { args, ret, throws, - tokens: _, + paren_token: _, + throws_tokens: _, } = self; receiver.hash(state); - args.hash(state); + for arg in args { + arg.hash(state); + } ret.hash(state); throws.hash(state); } diff --git a/syntax/mod.rs b/syntax/mod.rs index 38e21b0..0d0328b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -12,8 +12,11 @@ pub mod set; mod tokens; pub mod types; -use proc_macro2::{Ident, Span, TokenStream}; -use syn::{token::Brace, LitStr, Token}; +use self::parse::kw; +use proc_macro2::{Ident, Span}; +use syn::punctuated::Punctuated; +use syn::token::{Brace, Paren}; +use syn::{LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -55,10 +58,11 @@ pub struct ExternFn { pub struct Signature { pub fn_token: Token![fn], pub receiver: Option, - pub args: Vec, + pub args: Punctuated, pub ret: Option, pub throws: bool, - pub tokens: TokenStream, + pub paren_token: Paren, + pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>, } #[derive(Eq, PartialEq, Hash)] diff --git a/syntax/parse.rs b/syntax/parse.rs index a00f8dd..8e674bd 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -4,12 +4,17 @@ use crate::syntax::{ }; use proc_macro2::Ident; use quote::{format_ident, quote}; +use syn::punctuated::Punctuated; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, + ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, }; +pub mod kw { + syn::custom_keyword!(Result); +} + pub fn parse_items(items: Vec) -> Result> { let mut apis = Vec::new(); for item in items { @@ -151,8 +156,9 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { } let mut receiver = None; - let mut args = Vec::new(); - for arg in &foreign_fn.sig.inputs { + let mut args = Punctuated::new(); + for arg in foreign_fn.sig.inputs.pairs() { + let (arg, comma) = arg.into_tuple(); match arg { FnArg::Receiver(receiver) => { return Err(Error::new_spanned(receiver, "unsupported signature")) @@ -164,7 +170,10 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { }; let ty = parse_type(&arg.ty)?; if ident != "self" { - args.push(Var { ident, ty }); + args.push_value(Var { ident, ty }); + if let Some(comma) = comma { + args.push_punct(*comma); + } continue; } if let Type::Ref(reference) = ty { @@ -181,14 +190,13 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { } } - let mut throws = false; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws)?; + let mut throws_tokens = None; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; + let throws = throws_tokens.is_some(); let doc = attrs::parse_doc(&foreign_fn.attrs)?; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); - let mut foreign_fn2 = foreign_fn.clone(); - foreign_fn2.attrs.clear(); - let tokens = quote!(#foreign_fn2); + let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; Ok(ExternFn { @@ -201,7 +209,8 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { args, ret, throws, - tokens, + paren_token, + throws_tokens, }, semi_token, }) @@ -298,20 +307,24 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { Ok(Var { ident, ty }) }) .collect::>()?; - let mut throws = false; - let ret = parse_return_type(&ty.output, &mut throws)?; - let tokens = quote!(#ty); + let mut throws_tokens = None; + let ret = parse_return_type(&ty.output, &mut throws_tokens)?; + let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { fn_token: ty.fn_token, receiver: None, args, ret, throws, - tokens, + paren_token: ty.paren_token, + throws_tokens, }))) } -fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result> { +fn parse_return_type( + ty: &ReturnType, + throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, +) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), ReturnType::Type(_, ret) => ret.as_ref(), @@ -325,7 +338,8 @@ fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result> if ident == "Result" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { ret = arg; - *throws = true; + *throws_tokens = + Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token)); } } } diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 59bb0de..51592dd 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -63,12 +63,28 @@ impl ToTokens for Derive { impl ToTokens for ExternFn { fn to_tokens(&self, tokens: &mut TokenStream) { - self.sig.tokens.to_tokens(tokens); + // Notional token range for error reporting purposes. + self.sig.fn_token.to_tokens(tokens); + self.semi_token.to_tokens(tokens); } } impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { - self.tokens.to_tokens(tokens); + self.fn_token.to_tokens(tokens); + self.paren_token.surround(tokens, |tokens| { + self.args.to_tokens(tokens); + }); + if let Some(ret) = &self.ret { + Token![->](self.paren_token.span).to_tokens(tokens); + if let Some((result, langle, rangle)) = self.throws_tokens { + result.to_tokens(tokens); + langle.to_tokens(tokens); + ret.to_tokens(tokens); + rangle.to_tokens(tokens); + } else { + ret.to_tokens(tokens); + } + } } } From a23129c06994718dc70e7b1a81db56cb1a7e0c60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:08:28 +0000 Subject: [PATCH 217/2232] Allow calling fn pointers with explicit indirect call syntax --- diff --git a/include/cxx.h b/include/cxx.h index 3dd52e5..8bdbc90 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -155,6 +155,7 @@ template class Fn { public: Ret operator()(Args... args) noexcept(!Throws); + Fn operator*() noexcept; private: Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); @@ -204,5 +205,10 @@ Ret Fn::operator()(Args... args) noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); } +template +Fn Fn::operator*() noexcept { + return *this; +} + } // namespace cxxbridge02 } // namespace rust From c7673444ed5db49539a42897f18c2e61aab0388e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:18:55 +0000 Subject: [PATCH 218/2232] Merge pull request #89 from dtolnay/fncall Allow calling fn pointers with explicit indirect call syntax --- diff --git a/include/cxx.h b/include/cxx.h index 3dd52e5..8bdbc90 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -155,6 +155,7 @@ template class Fn { public: Ret operator()(Args... args) noexcept(!Throws); + Fn operator*() noexcept; private: Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); @@ -204,5 +205,10 @@ Ret Fn::operator()(Args... args) noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); } +template +Fn Fn::operator*() noexcept { + return *this; +} + } // namespace cxxbridge02 } // namespace rust From 23d8953ff3f94ae71b356a37d26472450f163430 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:21:25 +0000 Subject: [PATCH 219/2232] Lockfile update --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 59dbbb7..79cbae2 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.12" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +checksum = "8931031034aa65c73f3f1a05c3ec0fa51287fcd06557ecf4e88b2768bdca375e" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.12" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +checksum = "2147536f412ee7ae5529364ed50172ca0220fd64591e236296f45f36b38b2f98" dependencies = [ "proc-macro2", "quote", @@ -232,18 +232,18 @@ checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ "proc-macro2", "quote", @@ -252,9 +252,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" +checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" dependencies = [ "itoa", "ryu", @@ -269,9 +269,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d" +checksum = "ff6da2e8d107dfd7b74df5ef4d205c6aebee0706c647f6bc6a2d5789905c00fb" dependencies = [ "clap", "lazy_static", @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" +checksum = "a489c87c08fbaf12e386665109dd13470dcc9c4583ea3e10dd2b4523e5ebd9ac" dependencies = [ "heck", "proc-macro-error", @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b4e093c5ed1a60b22557090120aa14f90ca801549c0949d775ea07c1407720" +checksum = "459186ab1afd6d93bd23c2269125f4f7694f8771fe0e64434b4bdc212b94034d" dependencies = [ "glob", "lazy_static", From 93c51a6c05b0e06f4e5ab53c8ec0070fc3eca64e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:21:25 +0000 Subject: [PATCH 220/2232] Release 0.2.2 --- diff --git a/Cargo.toml b/Cargo.toml index 2cc6fff..1d3ec29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.1" # remember to update html_root_url +version = "0.2.2" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.1", path = "macro" } +cxxbridge-macro = { version = "=0.2.2", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index d100adc..ac90392 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.1" +version = "0.2.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 9456cda..5cf50a0 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.1" +version = "0.2.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 28f113a..27d67ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.1")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.2")] #![deny(improper_ctypes)] #![allow( clippy::declare_interior_mutable_const, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 79cbae2..4c83627 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.1" +version = "0.2.2" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.1" +version = "0.2.2" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.1" +version = "0.2.2" dependencies = [ "cxx", "proc-macro2", From 4ac0b189b3addc4508e31322c08bc8a26ae1c379 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:35:00 +0000 Subject: [PATCH 221/2232] Fix third-party targets files --- diff --git a/third-party/BUCK b/third-party/BUCK index 44c3a6a..483224b 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -59,7 +59,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.12/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.1/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -71,7 +71,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.12/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.1/src/**"]), proc_macro = True, deps = [ ":proc-macro2", @@ -107,7 +107,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.12/src/**"]), + srcs = glob(["vendor/structopt-0.3.13/src/**"]), visibility = ["PUBLIC"], deps = [ ":clap", @@ -118,7 +118,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.5/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.6/src/**"]), proc_macro = True, deps = [ ":heck", diff --git a/third-party/BUILD b/third-party/BUILD index 646289c..093d509 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -64,7 +64,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-0.4.12/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.1/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -76,7 +76,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-0.4.12/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.1/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", @@ -112,7 +112,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.12/src/**"]), + srcs = glob(["vendor/structopt-0.3.13/src/**"]), visibility = ["//visibility:public"], deps = [ ":clap", @@ -123,7 +123,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.5/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.6/src/**"]), crate_type = "proc-macro", deps = [ ":heck", From 533d458b43402c2b23c5adb9e13c20d0038c05c0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:36:13 +0000 Subject: [PATCH 222/2232] Fill in missing const on Fn member functions --- diff --git a/include/cxx.h b/include/cxx.h index 8bdbc90..5de479c 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -154,8 +154,8 @@ template class Fn; template class Fn { public: - Ret operator()(Args... args) noexcept(!Throws); - Fn operator*() noexcept; + Ret operator()(Args... args) const noexcept(!Throws); + Fn operator*() const noexcept; private: Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); @@ -201,12 +201,12 @@ constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE02_RUST_BITCOPY template -Ret Fn::operator()(Args... args) noexcept(!Throws) { +Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); } template -Fn Fn::operator*() noexcept { +Fn Fn::operator*() const noexcept { return *this; } From afb6f1387ae755d34ab2b84f64d614b4ff05ed22 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:50:17 +0000 Subject: [PATCH 223/2232] Merge pull request #90 from dtolnay/const Fill in missing const on Fn member functions --- diff --git a/include/cxx.h b/include/cxx.h index 8bdbc90..5de479c 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -154,8 +154,8 @@ template class Fn; template class Fn { public: - Ret operator()(Args... args) noexcept(!Throws); - Fn operator*() noexcept; + Ret operator()(Args... args) const noexcept(!Throws); + Fn operator*() const noexcept; private: Ret (*trampoline)(Args..., void *fn) noexcept(!Throws); @@ -201,12 +201,12 @@ constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE02_RUST_BITCOPY template -Ret Fn::operator()(Args... args) noexcept(!Throws) { +Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); } template -Fn Fn::operator*() noexcept { +Fn Fn::operator*() const noexcept { return *this; } From 86949cfdc6ec154c71dc64546f6ce4568c5e5247 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 03:51:19 +0000 Subject: [PATCH 224/2232] Release 0.2.3 --- diff --git a/Cargo.toml b/Cargo.toml index 1d3ec29..f127d5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.2" # remember to update html_root_url +version = "0.2.3" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.2", path = "macro" } +cxxbridge-macro = { version = "=0.2.3", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index ac90392..b44221d 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.2" +version = "0.2.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 5cf50a0..4cc82d0 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.2" +version = "0.2.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 27d67ca..f0f1449 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.2")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.3")] #![deny(improper_ctypes)] #![allow( clippy::declare_interior_mutable_const, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 4c83627..b453309 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.2" +version = "0.2.3" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.2" +version = "0.2.3" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.2" +version = "0.2.3" dependencies = [ "cxx", "proc-macro2", From 4ad8020fc4eec61e4cfb2b6efb75333ba1dbdf17 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 04:16:46 +0000 Subject: [PATCH 225/2232] Accept non-UnwindSafe extern Rust types in extern Rust signatures --- diff --git a/src/unwind.rs b/src/unwind.rs index 070aa28..abb7b92 100644 --- a/src/unwind.rs +++ b/src/unwind.rs @@ -1,12 +1,16 @@ use std::io::{self, Write}; -use std::panic::{self, UnwindSafe}; +use std::panic::{self, AssertUnwindSafe}; use std::process; pub fn catch_unwind(label: &'static str, foreign_call: F) -> R where - F: FnOnce() -> R + UnwindSafe, + F: FnOnce() -> R, { - match panic::catch_unwind(foreign_call) { + // Regarding the AssertUnwindSafe: we immediately abort on panic so it + // doesn't matter whether the types involved are unwind-safe. The UnwindSafe + // bound on catch_unwind is about ensuring nothing is in a broken state if + // your program plans to continue after the panic. + match panic::catch_unwind(AssertUnwindSafe(foreign_call)) { Ok(ret) => ret, Err(_) => abort(label), } From 48999f99370edcf2db2e8717b9af966f6cb3683a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 04:28:21 +0000 Subject: [PATCH 226/2232] Merge pull request #91 from dtolnay/unwindsafe Accept non-UnwindSafe extern Rust types in extern Rust signatures --- diff --git a/src/unwind.rs b/src/unwind.rs index 070aa28..abb7b92 100644 --- a/src/unwind.rs +++ b/src/unwind.rs @@ -1,12 +1,16 @@ use std::io::{self, Write}; -use std::panic::{self, UnwindSafe}; +use std::panic::{self, AssertUnwindSafe}; use std::process; pub fn catch_unwind(label: &'static str, foreign_call: F) -> R where - F: FnOnce() -> R + UnwindSafe, + F: FnOnce() -> R, { - match panic::catch_unwind(foreign_call) { + // Regarding the AssertUnwindSafe: we immediately abort on panic so it + // doesn't matter whether the types involved are unwind-safe. The UnwindSafe + // bound on catch_unwind is about ensuring nothing is in a broken state if + // your program plans to continue after the panic. + match panic::catch_unwind(AssertUnwindSafe(foreign_call)) { Ok(ret) => ret, Err(_) => abort(label), } From 57a1eaa5f1ba580936a1b85fc11220def0f461dd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 04:28:35 +0000 Subject: [PATCH 227/2232] Move panic abort message to stderr --- diff --git a/src/unwind.rs b/src/unwind.rs index abb7b92..36f6ae3 100644 --- a/src/unwind.rs +++ b/src/unwind.rs @@ -18,7 +18,7 @@ where #[cold] fn abort(label: &'static str) -> ! { - let mut stdout = io::stdout(); - let _ = writeln!(stdout, "Error: panic in ffi function {}, aborting.", label); + let mut stderr = io::stderr(); + let _ = writeln!(stderr, "Error: panic in ffi function {}, aborting.", label); process::abort(); } From b1637adafc50819f3030bc7163f055b5bd17f8f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 09 2020 04:29:14 +0000 Subject: [PATCH 228/2232] Release 0.2.4 --- diff --git a/Cargo.toml b/Cargo.toml index f127d5e..73c7e57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.3" # remember to update html_root_url +version = "0.2.4" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.3", path = "macro" } +cxxbridge-macro = { version = "=0.2.4", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index b44221d..ea936b3 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.3" +version = "0.2.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4cc82d0..4fd507e 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.3" +version = "0.2.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index f0f1449..86e9541 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.3")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.4")] #![deny(improper_ctypes)] #![allow( clippy::declare_interior_mutable_const, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b453309..8c8c6ef 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.3" +version = "0.2.4" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.3" +version = "0.2.4" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.3" +version = "0.2.4" dependencies = [ "cxx", "proc-macro2", From 5383891622e3a128f222b5f165a9cd8b1d3ef5e2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 03:56:44 +0000 Subject: [PATCH 229/2232] Do not emit UniquePtr::new for opaque C types --- diff --git a/gen/write.rs b/gen/write.rs index 3d1fdf3..217a591 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -766,7 +766,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, inner); + write_unique_ptr(out, inner, types); } } } @@ -830,7 +830,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { out.include.utility = true; let mut inner = String::new(); @@ -860,17 +860,19 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", - instance, inner, inner, - ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); + if types.structs.contains_key(ident) { + writeln!( + out, + "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + instance, inner, inner, + ); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } writeln!( out, "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1bb3628..e9a4495 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -57,7 +57,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, ident)); + expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } @@ -474,7 +474,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { +fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); @@ -483,6 +483,22 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); + let new_method = if types.structs.contains_key(ident) { + Some(quote! { + fn __new(mut value: Self) -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_new] + fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __new(&mut repr, &mut value) } + repr + } + }) + } else { + None + }; + quote! { unsafe impl ::cxx::private::UniquePtrTarget for #ident { fn __null() -> *mut ::std::ffi::c_void { @@ -494,15 +510,7 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { unsafe { __null(&mut repr) } repr } - fn __new(mut value: Self) -> *mut ::std::ffi::c_void { - extern "C" { - #[link_name = #link_new] - fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); - } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); - unsafe { __new(&mut repr, &mut value) } - repr - } + #new_method unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 94b6e25..4a1392a 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -118,7 +118,15 @@ pub unsafe trait UniquePtrTarget { #[doc(hidden)] fn __null() -> *mut c_void; #[doc(hidden)] - fn __new(value: Self) -> *mut c_void; + fn __new(value: Self) -> *mut c_void + where + Self: Sized, + { + // Opaque C types do not get this method because they can never exist by + // value on the Rust side of the bridge. + let _ = value; + unreachable!() + } #[doc(hidden)] unsafe fn __raw(raw: *mut Self) -> *mut c_void; #[doc(hidden)] From eb0ac1a583dc87aff4b2393428593a9357ed38d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 04:06:36 +0000 Subject: [PATCH 230/2232] Merge pull request #94 from dtolnay/uniquenew Do not emit UniquePtr::new for opaque C types --- diff --git a/gen/write.rs b/gen/write.rs index 3d1fdf3..217a591 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -766,7 +766,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, inner); + write_unique_ptr(out, inner, types); } } } @@ -830,7 +830,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { out.include.utility = true; let mut inner = String::new(); @@ -860,17 +860,19 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", - instance, inner, inner, - ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); + if types.structs.contains_key(ident) { + writeln!( + out, + "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + instance, inner, inner, + ); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } writeln!( out, "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1bb3628..e9a4495 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -57,7 +57,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, ident)); + expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } @@ -474,7 +474,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { +fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); @@ -483,6 +483,22 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); + let new_method = if types.structs.contains_key(ident) { + Some(quote! { + fn __new(mut value: Self) -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_new] + fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __new(&mut repr, &mut value) } + repr + } + }) + } else { + None + }; + quote! { unsafe impl ::cxx::private::UniquePtrTarget for #ident { fn __null() -> *mut ::std::ffi::c_void { @@ -494,15 +510,7 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident) -> TokenStream { unsafe { __null(&mut repr) } repr } - fn __new(mut value: Self) -> *mut ::std::ffi::c_void { - extern "C" { - #[link_name = #link_new] - fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); - } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); - unsafe { __new(&mut repr, &mut value) } - repr - } + #new_method unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 94b6e25..4a1392a 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -118,7 +118,15 @@ pub unsafe trait UniquePtrTarget { #[doc(hidden)] fn __null() -> *mut c_void; #[doc(hidden)] - fn __new(value: Self) -> *mut c_void; + fn __new(value: Self) -> *mut c_void + where + Self: Sized, + { + // Opaque C types do not get this method because they can never exist by + // value on the Rust side of the bridge. + let _ = value; + unreachable!() + } #[doc(hidden)] unsafe fn __raw(raw: *mut Self) -> *mut c_void; #[doc(hidden)] From 5f1cc8aa44210f934a2fe810ac334ea9da9f530d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 04:07:36 +0000 Subject: [PATCH 231/2232] Release 0.2.5 --- diff --git a/Cargo.toml b/Cargo.toml index 73c7e57..851fde8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.4" # remember to update html_root_url +version = "0.2.5" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.4", path = "macro" } +cxxbridge-macro = { version = "=0.2.5", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index ea936b3..8e46782 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.4" +version = "0.2.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4fd507e..a965cf2 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.4" +version = "0.2.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 86e9541..a254581 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.4")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.5")] #![deny(improper_ctypes)] #![allow( clippy::declare_interior_mutable_const, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 8c8c6ef..5c2cc5f 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.4" +version = "0.2.5" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.4" +version = "0.2.5" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.4" +version = "0.2.5" dependencies = [ "cxx", "proc-macro2", @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8931031034aa65c73f3f1a05c3ec0fa51287fcd06557ecf4e88b2768bdca375e" +checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2147536f412ee7ae5529364ed50172ca0220fd64591e236296f45f36b38b2f98" +checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" dependencies = [ "proc-macro2", "quote", From 25b8c8f1cbd719b4be736d8369262fc3897f1629 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 04:12:27 +0000 Subject: [PATCH 232/2232] Remove >::new CxxString is considered an opaque C type and can never exist in Rust by value, so this constructor was uncallable. --- diff --git a/src/cxx.cc b/src/cxx.cc index 0c1b8af..35dcc95 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -169,10 +169,6 @@ void cxxbridge02$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge02$unique_ptr$std$string$new(std::unique_ptr *ptr, - std::string *value) noexcept { - new (ptr) std::unique_ptr(new std::string(std::move(*value))); -} void cxxbridge02$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 4a1392a..f058d5e 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -2,7 +2,7 @@ use crate::cxx_string::CxxString; use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::marker::PhantomData; -use std::mem::{self, MaybeUninit}; +use std::mem; use std::ptr; /// Binding to C++ `std::unique_ptr>`. @@ -140,8 +140,6 @@ pub unsafe trait UniquePtrTarget { extern "C" { #[link_name = "cxxbridge02$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge02$unique_ptr$std$string$new"] - fn unique_ptr_std_string_new(this: *mut *mut c_void, value: *mut CxxString); #[link_name = "cxxbridge02$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); #[link_name = "cxxbridge02$unique_ptr$std$string$get"] @@ -158,12 +156,6 @@ unsafe impl UniquePtrTarget for CxxString { unsafe { unique_ptr_std_string_null(&mut repr) } repr } - fn __new(value: Self) -> *mut c_void { - let mut repr = ptr::null_mut::(); - let mut value = MaybeUninit::new(value); - unsafe { unique_ptr_std_string_new(&mut repr, value.as_mut_ptr() as *mut Self) } - repr - } unsafe fn __raw(raw: *mut Self) -> *mut c_void { let mut repr = ptr::null_mut::(); unique_ptr_std_string_raw(&mut repr, raw); From 4bacd3cc67f3a285a36b994bb902eb83842f80fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 04:17:51 +0000 Subject: [PATCH 233/2232] Update targets files to proc-macro-error 1.0.2 --- diff --git a/third-party/BUCK b/third-party/BUCK index 483224b..3bc34db 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -59,7 +59,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.1/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.2/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -71,7 +71,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.1/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.2/src/**"]), proc_macro = True, deps = [ ":proc-macro2", diff --git a/third-party/BUILD b/third-party/BUILD index 093d509..8e47d3d 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -64,7 +64,7 @@ rust_library( rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.1/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.2/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -76,7 +76,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.1/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.2/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", From 1a61b16007524c006e80f4da10cc12ce277e5d75 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:20:05 +0000 Subject: [PATCH 234/2232] Provide UniquePtr::as_mut --- diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index f058d5e..ca527d2 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -51,6 +51,12 @@ where unsafe { T::__get(self.repr).as_ref() } } + /// Returns a mutable reference to the object owned by this UniquePtr if + /// any, otherwise None. + pub fn as_mut(&mut self) -> Option<&mut T> { + unsafe { (T::__get(self.repr) as *mut T).as_mut() } + } + /// Consumes the UniquePtr, releasing its ownership of the heap-allocated T. /// /// Matches the behavior of [std::unique_ptr\::release](https://en.cppreference.com/w/cpp/memory/unique_ptr/release). From 4bc9815c6aeda5a4d09a7971afc2d490a24cc41c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:26:16 +0000 Subject: [PATCH 235/2232] Suppress clippy::cognitive_complexity lint --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 3577f72..d0a215e 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -1,4 +1,5 @@ #![allow( + clippy::cognitive_complexity, clippy::inherent_to_string, clippy::large_enum_variant, clippy::new_without_default, diff --git a/src/lib.rs b/src/lib.rs index a254581..8b3a5a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -340,6 +340,7 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.2.5")] #![deny(improper_ctypes)] #![allow( + clippy::cognitive_complexity, clippy::declare_interior_mutable_const, clippy::inherent_to_string, clippy::large_enum_variant, From a95b2341d085375950dc726475c87ebf5d19322f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:26:16 +0000 Subject: [PATCH 236/2232] Suppress clippy::ptr_arg triggering on &String in tests --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 7df0110..68651ea 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -1,4 +1,8 @@ -#![allow(clippy::boxed_local, clippy::trivially_copy_pass_by_ref)] +#![allow( + clippy::boxed_local, + clippy::ptr_arg, + clippy::trivially_copy_pass_by_ref +)] use cxx::{CxxString, UniquePtr}; use std::fmt::{self, Display}; From d20032a1727f3fd5b731be1c14604af734526f9e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:42:45 +0000 Subject: [PATCH 237/2232] Deref and DerefMut for UniquePtr --- diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index c8c5a67..ab8b9ef 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -3,6 +3,7 @@ use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::marker::PhantomData; use std::mem; +use std::ops::{Deref, DerefMut}; use std::ptr; /// Binding to C++ `std::unique_ptr>`. @@ -94,6 +95,32 @@ where } } +impl Deref for UniquePtr +where + T: UniquePtrTarget, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + match self.as_ref() { + Some(target) => target, + None => panic!("called deref on a null UniquePtr"), + } + } +} + +impl DerefMut for UniquePtr +where + T: UniquePtrTarget, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + match self.as_mut() { + Some(target) => target, + None => panic!("called deref_mut on a null UniquePtr"), + } + } +} + impl Debug for UniquePtr where T: Debug + UniquePtrTarget, From ad26677f7bfe58f4bf9fe4d4ca96b85f84b4e1fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:42:45 +0000 Subject: [PATCH 238/2232] Include type name in UniquePtr function table --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e9a4495..67c34e5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -475,6 +475,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { + let name = ident.to_string(); let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); @@ -501,6 +502,7 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok quote! { unsafe impl ::cxx::private::UniquePtrTarget for #ident { + const __NAME: &'static str = #name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_null] diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index ca527d2..c8c5a67 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -122,6 +122,8 @@ where // codebase. pub unsafe trait UniquePtrTarget { #[doc(hidden)] + const __NAME: &'static str; + #[doc(hidden)] fn __null() -> *mut c_void; #[doc(hidden)] fn __new(value: Self) -> *mut c_void @@ -157,6 +159,7 @@ extern "C" { } unsafe impl UniquePtrTarget for CxxString { + const __NAME: &'static str = "CxxString"; fn __null() -> *mut c_void { let mut repr = ptr::null_mut::(); unsafe { unique_ptr_std_string_null(&mut repr) } From 9f318a10ad47f60e32cedb60c1c6a969313eb50b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:44:02 +0000 Subject: [PATCH 239/2232] Show UniquePtr target type name in panics --- diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index ab8b9ef..b50e870 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -104,7 +104,7 @@ where fn deref(&self) -> &Self::Target { match self.as_ref() { Some(target) => target, - None => panic!("called deref on a null UniquePtr"), + None => panic!("called deref on a null UniquePtr<{}>", T::__NAME), } } } @@ -116,7 +116,7 @@ where fn deref_mut(&mut self) -> &mut Self::Target { match self.as_mut() { Some(target) => target, - None => panic!("called deref_mut on a null UniquePtr"), + None => panic!("called deref_mut on a null UniquePtr<{}>", T::__NAME), } } } From b5609f86cadb7f1ce80640a1ec3322ef9f9b2bb2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:44:23 +0000 Subject: [PATCH 240/2232] Add UniquePtr test --- diff --git a/tests/unique_ptr.rs b/tests/unique_ptr.rs new file mode 100644 index 0000000..e5eb66b --- /dev/null +++ b/tests/unique_ptr.rs @@ -0,0 +1,8 @@ +use cxx::{CxxString, UniquePtr}; + +#[test] +#[should_panic = "called deref on a null UniquePtr"] +fn test_deref_null() { + let unique_ptr = UniquePtr::::null(); + let _: &CxxString = &unique_ptr; +} From b455d0a0a176a747301060eebfa096d978b2c0ef Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 06:58:38 +0000 Subject: [PATCH 241/2232] Merge pull request #95 from dtolnay/deref Deref and DerefMut for UniquePtr --- diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index c8c5a67..b50e870 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -3,6 +3,7 @@ use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::marker::PhantomData; use std::mem; +use std::ops::{Deref, DerefMut}; use std::ptr; /// Binding to C++ `std::unique_ptr>`. @@ -94,6 +95,32 @@ where } } +impl Deref for UniquePtr +where + T: UniquePtrTarget, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + match self.as_ref() { + Some(target) => target, + None => panic!("called deref on a null UniquePtr<{}>", T::__NAME), + } + } +} + +impl DerefMut for UniquePtr +where + T: UniquePtrTarget, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + match self.as_mut() { + Some(target) => target, + None => panic!("called deref_mut on a null UniquePtr<{}>", T::__NAME), + } + } +} + impl Debug for UniquePtr where T: Debug + UniquePtrTarget, diff --git a/tests/unique_ptr.rs b/tests/unique_ptr.rs new file mode 100644 index 0000000..e5eb66b --- /dev/null +++ b/tests/unique_ptr.rs @@ -0,0 +1,8 @@ +use cxx::{CxxString, UniquePtr}; + +#[test] +#[should_panic = "called deref on a null UniquePtr"] +fn test_deref_null() { + let unique_ptr = UniquePtr::::null(); + let _: &CxxString = &unique_ptr; +} From 4b972729954b19d0b8535b550997e5caec7fc901 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 07:48:18 +0000 Subject: [PATCH 242/2232] Release 0.2.6 --- diff --git a/Cargo.toml b/Cargo.toml index 851fde8..fa5fa88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.5" # remember to update html_root_url +version = "0.2.6" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.5", path = "macro" } +cxxbridge-macro = { version = "=0.2.6", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 8e46782..9b15e2e 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.5" +version = "0.2.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index a965cf2..fe87735 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.5" +version = "0.2.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 8b3a5a0..1904cf6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.5")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.6")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 5c2cc5f..4190f2a 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.5" +version = "0.2.6" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.5" +version = "0.2.6" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.5" +version = "0.2.6" dependencies = [ "cxx", "proc-macro2", From b8a6fb27559bf27c80e0a9f1ab899f8545002d8b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 18:17:28 +0000 Subject: [PATCH 243/2232] Define rust::isize with Windows support --- diff --git a/gen/write.rs b/gen/write.rs index 217a591..9d6ef6e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -110,6 +110,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_str = false; let mut needs_rust_box = false; let mut needs_rust_fn = false; + let mut needs_rust_isize = false; for ty in types { match ty { Type::RustBox(_) => { @@ -124,6 +125,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Type::Fn(_) => { needs_rust_fn = true; } + ty if ty == Isize => { + needs_rust_isize = true; + } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -181,6 +185,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_rust_box || needs_rust_fn || needs_rust_error + || needs_rust_isize || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit @@ -199,6 +204,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); if needs_manually_drop { @@ -689,7 +695,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I16) => write!(out, "int16_t"), Some(I32) => write!(out, "int32_t"), Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "ssize_t"), + Some(Isize) => write!(out, "::rust::isize"), Some(F32) => write!(out, "float"), Some(F64) => write!(out, "double"), Some(CxxString) => write!(out, "::std::string"), diff --git a/include/cxx.h b/include/cxx.h index 5de479c..6fe55d0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -180,6 +180,15 @@ private: }; #endif // CXXBRIDGE02_RUST_ERROR +#ifndef CXXBRIDGE02_RUST_ISIZE +#define CXXBRIDGE02_RUST_ISIZE +#if defined(_WIN32) +using isize = SSIZE_T; +#else +using isize = ssize_t; +#endif +#endif // CXXBRIDGE02_RUST_ISIZE + std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); From 59b5ba1f1433a89865f52e2cbaa231a0f7ad1940 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 18:36:08 +0000 Subject: [PATCH 244/2232] Include BaseTsd.h to get SSIZE_T --- diff --git a/gen/include.rs b/gen/include.rs index e3a9dd7..8f38fe3 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -38,6 +38,7 @@ pub struct Includes { pub string: bool, pub type_traits: bool, pub utility: bool, + pub base_tsd: bool, } impl Includes { @@ -88,6 +89,11 @@ impl Display for Includes { if self.utility { writeln!(f, "#include ")?; } + if self.base_tsd { + writeln!(f, "#if defined(_WIN32)")?; + writeln!(f, "#include ")?; + writeln!(f, "#endif")?; + } if *self != Self::default() { writeln!(f)?; } diff --git a/gen/write.rs b/gen/write.rs index 9d6ef6e..d80e957 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -126,6 +126,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_fn = true; } ty if ty == Isize => { + out.include.base_tsd = true; needs_rust_isize = true; } ty if ty == RustString => { diff --git a/include/cxx.h b/include/cxx.h index 6fe55d0..27034f0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -7,6 +7,9 @@ #include #include #include +#if defined(_WIN32) +#include +#endif namespace rust { inline namespace cxxbridge02 { From 59b7c12ffc75c45f16009da2ae1119a66f1ec910 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 19:04:08 +0000 Subject: [PATCH 245/2232] Merge pull request #97 from dtolnay/isize Define rust::isize with Windows support --- diff --git a/gen/include.rs b/gen/include.rs index e3a9dd7..8f38fe3 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -38,6 +38,7 @@ pub struct Includes { pub string: bool, pub type_traits: bool, pub utility: bool, + pub base_tsd: bool, } impl Includes { @@ -88,6 +89,11 @@ impl Display for Includes { if self.utility { writeln!(f, "#include ")?; } + if self.base_tsd { + writeln!(f, "#if defined(_WIN32)")?; + writeln!(f, "#include ")?; + writeln!(f, "#endif")?; + } if *self != Self::default() { writeln!(f)?; } diff --git a/gen/write.rs b/gen/write.rs index 217a591..d80e957 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -110,6 +110,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_str = false; let mut needs_rust_box = false; let mut needs_rust_fn = false; + let mut needs_rust_isize = false; for ty in types { match ty { Type::RustBox(_) => { @@ -124,6 +125,10 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Type::Fn(_) => { needs_rust_fn = true; } + ty if ty == Isize => { + out.include.base_tsd = true; + needs_rust_isize = true; + } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -181,6 +186,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_rust_box || needs_rust_fn || needs_rust_error + || needs_rust_isize || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit @@ -199,6 +205,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); if needs_manually_drop { @@ -689,7 +696,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Some(I16) => write!(out, "int16_t"), Some(I32) => write!(out, "int32_t"), Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "ssize_t"), + Some(Isize) => write!(out, "::rust::isize"), Some(F32) => write!(out, "float"), Some(F64) => write!(out, "double"), Some(CxxString) => write!(out, "::std::string"), diff --git a/include/cxx.h b/include/cxx.h index 5de479c..27034f0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -7,6 +7,9 @@ #include #include #include +#if defined(_WIN32) +#include +#endif namespace rust { inline namespace cxxbridge02 { @@ -180,6 +183,15 @@ private: }; #endif // CXXBRIDGE02_RUST_ERROR +#ifndef CXXBRIDGE02_RUST_ISIZE +#define CXXBRIDGE02_RUST_ISIZE +#if defined(_WIN32) +using isize = SSIZE_T; +#else +using isize = ssize_t; +#endif +#endif // CXXBRIDGE02_RUST_ISIZE + std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); From 40b932fc455824039fe0a1fb08896b90e7265436 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 19:08:11 +0000 Subject: [PATCH 246/2232] Release 0.2.7 --- diff --git a/Cargo.toml b/Cargo.toml index fa5fa88..c92e1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.6" # remember to update html_root_url +version = "0.2.7" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.6", path = "macro" } +cxxbridge-macro = { version = "=0.2.7", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 9b15e2e..9a6b8f5 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.6" +version = "0.2.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index fe87735..f9a9904 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.6" +version = "0.2.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 1904cf6..2d8facf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.6")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.7")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 4190f2a..a6d15ad 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.6" +version = "0.2.7" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.6" +version = "0.2.7" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.6" +version = "0.2.7" dependencies = [ "cxx", "proc-macro2", From 8e0866125c1974c0425f928728a5e4b9c1ebaee7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 19:20:49 +0000 Subject: [PATCH 247/2232] Verify that header sections are found even if not used This prevents typos in the endif part of the section even when test coverage is not perfect. --- diff --git a/gen/write.rs b/gen/write.rs index d80e957..4eed1b4 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -261,9 +261,10 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { + let section = include::get(section); if needed { out.next_section(); - for line in include::get(section).lines() { + for line in section.lines() { if !line.trim_start().starts_with("//") { writeln!(out, "{}", line); } From 8e2771409d1c8a4e4b97798d48f2682cc33bcdbd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 10 2020 20:35:51 +0000 Subject: [PATCH 248/2232] Add a CI build on our minimum supported compiler --- diff --git a/.travis.yml b/.travis.yml index 1cc7806..28ad110 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,3 +53,5 @@ matrix: script: - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress + - name: Minimum rustc + rust: 1.42.0 From ce5af5485b3ffa8982b903e4a39f5435c3447f96 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 01:08:30 +0000 Subject: [PATCH 249/2232] Accept RUST_CXX_NO_EXCEPTIONS to disable throwing --- diff --git a/src/cxx.cc b/src/cxx.cc index 35dcc95..6656041 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,9 +1,20 @@ #include "../include/cxx.h" +#include #include #include #include #include +template +[[noreturn]] static void panic(const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::cerr << "Error: " << msg << ". Aborting." << std::endl; + std::abort(); +#else + throw Exception(msg); +#endif +} + extern "C" { const char *cxxbridge02$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -47,14 +58,14 @@ String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); if (!cxxbridge02$string$from(this, ptr, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); + panic("data for rust::String is not utf-8"); } } String::String(const char *s) { auto len = std::strlen(s); if (!cxxbridge02$string$from(this, s, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); + panic("data for rust::String is not utf-8"); } } @@ -101,13 +112,13 @@ Str::Str(const Str &) noexcept = default; Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); + panic("data for rust::Str is not utf-8"); } } Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); + panic("data for rust::Str is not utf-8"); } } From 31b5aad39b16bab7634c39a1a8c7db082cd7d5b0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 02:35:47 +0000 Subject: [PATCH 250/2232] Use more obvious words as the "name in C++" of Result --- diff --git a/README.md b/README.md index b5c57fd..0b3aab3 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ returns of functions. Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far -Result<T>error <=> exceptionallowed as return type only +Result<T>throw/catchallowed as return type only The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in diff --git a/src/lib.rs b/src/lib.rs index 2d8facf..f918b29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -311,7 +311,7 @@ //! Box<T>rust::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type //! fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far -//! Result<T>error <=> exceptionallowed as return type only +//! Result<T>throw/catchallowed as return type only //! //! //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file From 47b3cf22836024d088909770e17d7f14c0295f81 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 07:54:39 +0000 Subject: [PATCH 251/2232] Add constructor to move T into Box --- diff --git a/include/cxx.h b/include/cxx.h index 27034f0..338dafe 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -98,6 +98,10 @@ public: this->uninit(); ::new (this->ptr) T(val); } + Box(T &&val) { + this->uninit(); + ::new (this->ptr) T(std::move(val)); + } Box &operator=(const Box &other) { if (this != &other) { if (this->ptr) { From 7ce59fc9e86e42d804243e04957db969a64e93ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 18:53:29 +0000 Subject: [PATCH 252/2232] Add in_place constructor for Box --- diff --git a/include/cxx.h b/include/cxx.h index 338dafe..157c31e 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -132,6 +132,14 @@ public: T *operator->() noexcept { return this->ptr; } T &operator*() noexcept { return *this->ptr; } + template + static Box in_place(Fields&&... fields) { + Box box; + box.uninit(); + ::new (box.ptr) T{std::forward(fields)...}; + return box; + } + // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. static Box from_raw(T *raw) noexcept { From bbbce5a7d0bcac0bb4d4a044fafe8a0b03603384 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 21:10:24 +0000 Subject: [PATCH 253/2232] Merge pull request #101 from dtolnay/no-exceptions Accept RUST_CXX_NO_EXCEPTIONS to disable throwing --- diff --git a/src/cxx.cc b/src/cxx.cc index 35dcc95..6656041 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,9 +1,20 @@ #include "../include/cxx.h" +#include #include #include #include #include +template +[[noreturn]] static void panic(const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::cerr << "Error: " << msg << ". Aborting." << std::endl; + std::abort(); +#else + throw Exception(msg); +#endif +} + extern "C" { const char *cxxbridge02$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -47,14 +58,14 @@ String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); if (!cxxbridge02$string$from(this, ptr, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); + panic("data for rust::String is not utf-8"); } } String::String(const char *s) { auto len = std::strlen(s); if (!cxxbridge02$string$from(this, s, len)) { - throw std::invalid_argument("data for rust::String is not utf-8"); + panic("data for rust::String is not utf-8"); } } @@ -101,13 +112,13 @@ Str::Str(const Str &) noexcept = default; Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); + panic("data for rust::Str is not utf-8"); } } Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { - throw std::invalid_argument("data for rust::Str is not utf-8"); + panic("data for rust::Str is not utf-8"); } } From dd8d06c4b9ffe3eacb06bda051250c1a1682ddcd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 21:10:48 +0000 Subject: [PATCH 254/2232] Merge pull request #106 from dtolnay/inplace Add in_place constructor for Box --- diff --git a/include/cxx.h b/include/cxx.h index 338dafe..157c31e 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -132,6 +132,14 @@ public: T *operator->() noexcept { return this->ptr; } T &operator*() noexcept { return *this->ptr; } + template + static Box in_place(Fields&&... fields) { + Box box; + box.uninit(); + ::new (box.ptr) T{std::forward(fields)...}; + return box; + } + // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. static Box from_raw(T *raw) noexcept { From 7db7dade2481ff7113242b943a0f2362aeaacc5a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 21:12:49 +0000 Subject: [PATCH 255/2232] Make it explicit when going from T to Box --- diff --git a/include/cxx.h b/include/cxx.h index 157c31e..2ffacc5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -94,11 +94,11 @@ public: Box(const Box &other) : Box(*other) {} Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - Box(const T &val) { + explicit Box(const T &val) { this->uninit(); ::new (this->ptr) T(val); } - Box(T &&val) { + explicit Box(T &&val) { this->uninit(); ::new (this->ptr) T(std::move(val)); } From d678cddb1e49eb25f54a8e88ac5ad840854e08d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 11 2020 21:38:07 +0000 Subject: [PATCH 256/2232] Merge pull request #107 from dtolnay/explicit Make it explicit when going from T to Box --- diff --git a/include/cxx.h b/include/cxx.h index 157c31e..2ffacc5 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -94,11 +94,11 @@ public: Box(const Box &other) : Box(*other) {} Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - Box(const T &val) { + explicit Box(const T &val) { this->uninit(); ::new (this->ptr) T(val); } - Box(T &&val) { + explicit Box(T &&val) { this->uninit(); ::new (this->ptr) T(std::move(val)); } From 71918ec8224b6e68400abc70d66f58c6bfec08b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 12 2020 04:52:09 +0000 Subject: [PATCH 257/2232] Use terminate instead of abort in no-exceptions mode --- diff --git a/src/cxx.cc b/src/cxx.cc index 6656041..8834581 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,6 +1,6 @@ #include "../include/cxx.h" -#include #include +#include #include #include #include @@ -9,7 +9,7 @@ template [[noreturn]] static void panic(const char *msg) { #if defined(RUST_CXX_NO_EXCEPTIONS) std::cerr << "Error: " << msg << ". Aborting." << std::endl; - std::abort(); + std::terminate(); #else throw Exception(msg); #endif From a3d92bf9924d4d1277654c885d6ed09bcb0a7969 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 12 2020 05:04:13 +0000 Subject: [PATCH 258/2232] Merge pull request #112 from dtolnay/terminate Use terminate instead of abort in no-exceptions mode --- diff --git a/src/cxx.cc b/src/cxx.cc index 6656041..8834581 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,6 +1,6 @@ #include "../include/cxx.h" -#include #include +#include #include #include #include @@ -9,7 +9,7 @@ template [[noreturn]] static void panic(const char *msg) { #if defined(RUST_CXX_NO_EXCEPTIONS) std::cerr << "Error: " << msg << ". Aborting." << std::endl; - std::abort(); + std::terminate(); #else throw Exception(msg); #endif From c2279000a926db64c0bb0caa69025031174d576a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 12 2020 05:06:00 +0000 Subject: [PATCH 259/2232] Format with clangfmt --- diff --git a/include/cxx.h b/include/cxx.h index 2ffacc5..f7decd8 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -132,8 +132,7 @@ public: T *operator->() noexcept { return this->ptr; } T &operator*() noexcept { return *this->ptr; } - template - static Box in_place(Fields&&... fields) { + template static Box in_place(Fields &&... fields) { Box box; box.uninit(); ::new (box.ptr) T{std::forward(fields)...}; diff --git a/src/cxx.cc b/src/cxx.cc index 8834581..e8ec979 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -5,8 +5,7 @@ #include #include -template -[[noreturn]] static void panic(const char *msg) { +template static void panic [[noreturn]] (const char *msg) { #if defined(RUST_CXX_NO_EXCEPTIONS) std::cerr << "Error: " << msg << ". Aborting." << std::endl; std::terminate(); From f262d38671d156a2de089ae512d6e325b20dc564 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 12 2020 05:14:44 +0000 Subject: [PATCH 260/2232] AlwaysBreakTemplateDeclarations: true I felt that the clangfmt changes in c2279000a926db64c0bb0caa69025031174d576a were not making the code better. --- diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..181605d --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +AlwaysBreakTemplateDeclarations: true diff --git a/include/cxx.h b/include/cxx.h index f7decd8..a8239a1 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -85,7 +85,8 @@ private: #ifndef CXXBRIDGE02_RUST_BOX #define CXXBRIDGE02_RUST_BOX -template class Box final { +template +class Box final { public: using value_type = T; using const_pointer = typename std::add_pointer< @@ -132,7 +133,8 @@ public: T *operator->() noexcept { return this->ptr; } T &operator*() noexcept { return *this->ptr; } - template static Box in_place(Fields &&... fields) { + template + static Box in_place(Fields &&... fields) { Box box; box.uninit(); ::new (box.ptr) T{std::forward(fields)...}; @@ -163,7 +165,8 @@ private: #ifndef CXXBRIDGE02_RUST_FN #define CXXBRIDGE02_RUST_FN -template class Fn; +template +class Fn; template class Fn { @@ -176,7 +179,8 @@ private: void *fn; }; -template using TryFn = Fn; +template +using TryFn = Fn; #endif // CXXBRIDGE02_RUST_FN #ifndef CXXBRIDGE02_RUST_ERROR @@ -209,11 +213,13 @@ std::ostream &operator<<(std::ostream &, const Str &); // Snake case aliases for use in code that uses this style for type names. using string = String; using str = Str; -template using box = Box; +template +using box = Box; using error = Error; template using fn = Fn; -template using try_fn = TryFn; +template +using try_fn = TryFn; #ifndef CXXBRIDGE02_RUST_BITCOPY #define CXXBRIDGE02_RUST_BITCOPY diff --git a/src/cxx.cc b/src/cxx.cc index e8ec979..b64333c 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -5,7 +5,8 @@ #include #include -template static void panic [[noreturn]] (const char *msg) { +template +static void panic [[noreturn]] (const char *msg) { #if defined(RUST_CXX_NO_EXCEPTIONS) std::cerr << "Error: " << msg << ". Aborting." << std::endl; std::terminate(); From 7f635f360e279d3c3a00f78a8911cca32d90991a Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Apr 13 2020 05:04:11 +0000 Subject: [PATCH 261/2232] Specify author explicitly. For those building without Cargo, it may be a little awkward to specify the CARGO_PKG_AUTHORS environment variable (in some cases perhaps requiring a wrapper script around rustc). --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index d0a215e..8c407ad 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -17,7 +17,7 @@ use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt( name = "cxxbridge", - author, + author = "dtolnay@gmail.com", about = "https://github.com/dtolnay/cxx", usage = "\ cxxbridge .rs Emit .cc file for bridge to stdout From 60a30832318343afec4df3757df9f521fe91b833 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 13 2020 06:19:37 +0000 Subject: [PATCH 262/2232] Merge pull request #113 from adetaylor/with-author Specify author explicitly. --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index d0a215e..8c407ad 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -17,7 +17,7 @@ use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt( name = "cxxbridge", - author, + author = "dtolnay@gmail.com", about = "https://github.com/dtolnay/cxx", usage = "\ cxxbridge .rs Emit .cc file for bridge to stdout From bf787c587eb4b9a94d2ce88e1fb1248dc2ed3321 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 13 2020 06:20:21 +0000 Subject: [PATCH 263/2232] Update cmd author to match author in Cargo.toml --- diff --git a/cmd/src/main.rs b/cmd/src/main.rs index 8c407ad..a20179f 100644 --- a/cmd/src/main.rs +++ b/cmd/src/main.rs @@ -17,7 +17,7 @@ use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt( name = "cxxbridge", - author = "dtolnay@gmail.com", + author = "David Tolnay ", about = "https://github.com/dtolnay/cxx", usage = "\ cxxbridge .rs Emit .cc file for bridge to stdout From efa9edae9f84ecf7eb596374a357544e127bc8bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 03:30:12 +0000 Subject: [PATCH 264/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 3bc34db..af14713 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -171,14 +171,14 @@ rust_library( rust_library( name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.14/src/**"]), + srcs = glob(["vendor/thiserror-1.0.15/src/**"]), visibility = ["PUBLIC"], deps = [":thiserror-impl"], ) rust_library( name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.14/src/**"]), + srcs = glob(["vendor/thiserror-impl-1.0.15/src/**"]), proc_macro = True, deps = [ ":proc-macro2", diff --git a/third-party/BUILD b/third-party/BUILD index 8e47d3d..25a3dd6 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -176,14 +176,14 @@ rust_library( rust_library( name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.14/src/**"]), + srcs = glob(["vendor/thiserror-1.0.15/src/**"]), visibility = ["//visibility:public"], deps = [":thiserror-impl"], ) rust_library( name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.14/src/**"]), + srcs = glob(["vendor/thiserror-impl-1.0.15/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index a6d15ad..48552bc 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -156,9 +156,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" [[package]] name = "link-cplusplus" @@ -333,18 +333,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0570dc61221295909abdb95c739f2e74325e14293b2026b0a7e195091ec54ae" +checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd" +checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd" dependencies = [ "proc-macro2", "quote", From 48b09e94ef034dc155bb32c42ad25fa097fdd195 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 03:31:09 +0000 Subject: [PATCH 265/2232] Release 0.2.8 --- diff --git a/Cargo.toml b/Cargo.toml index c92e1c8..044095b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.7" # remember to update html_root_url +version = "0.2.8" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.7", path = "macro" } +cxxbridge-macro = { version = "=0.2.8", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 9a6b8f5..4a4cde2 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.7" +version = "0.2.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index f9a9904..c85062f 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.7" +version = "0.2.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index f918b29..7ee2bfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.7")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.8")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 48552bc..fce3ce0 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.7" +version = "0.2.8" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.7" +version = "0.2.8" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.7" +version = "0.2.8" dependencies = [ "cxx", "proc-macro2", From 9b894fb96e6155166823a0e09140eef3fef48952 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 18:13:25 +0000 Subject: [PATCH 266/2232] Handle failures to symlink on windows --- diff --git a/src/paths.rs b/src/paths.rs index 62fbb6e..ca183d9 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -1,7 +1,6 @@ use crate::error::{Error, Result}; use std::env; use std::fs; -use std::os; use std::path::{Path, PathBuf}; fn out_dir() -> Result { @@ -28,22 +27,17 @@ pub(crate) fn symlink_header(path: &Path, original: &Path) { } fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { - #[cfg(unix)] - use os::unix::fs::symlink; - #[cfg(windows)] - use os::windows::fs::symlink_file as symlink; - let suffix = relative_to_parent_of_target_dir(original)?; let ref dst = include_dir()?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); - symlink(path, dst)?; + symlink_or_copy(path, dst)?; let mut file_name = dst.file_name().unwrap().to_os_string(); file_name.push(".h"); - let dst2 = dst.with_file_name(file_name); - symlink(path, dst2)?; + let ref dst2 = dst.with_file_name(file_name); + symlink_or_copy(path, dst2)?; Ok(()) } @@ -102,3 +96,21 @@ fn canonicalize(path: impl AsRef) -> Result { // https://github.com/alexcrichton/cc-rs/issues/169 Ok(env::current_dir()?.join(path)) } + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_or_copy; + +#[cfg(windows)] +fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { + use std::os::windows::fs::symlink_file; + + // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they + // require Developer Mode. If it fails, fall back to copying the file. + if symlink_file(src, dst).is_err() { + fs::copy(src, dst)?; + } + Ok(()) +} + +#[cfg(not(any(unix, windows)))] +use std::fs::copy as symlink_or_copy; From 850ca90849e7fb2c045fecdd428f865686e3bb4c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 18:53:00 +0000 Subject: [PATCH 267/2232] Merge pull request #116 from dtolnay/symlink Handle failures to symlink on windows --- diff --git a/src/paths.rs b/src/paths.rs index 62fbb6e..ca183d9 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -1,7 +1,6 @@ use crate::error::{Error, Result}; use std::env; use std::fs; -use std::os; use std::path::{Path, PathBuf}; fn out_dir() -> Result { @@ -28,22 +27,17 @@ pub(crate) fn symlink_header(path: &Path, original: &Path) { } fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { - #[cfg(unix)] - use os::unix::fs::symlink; - #[cfg(windows)] - use os::windows::fs::symlink_file as symlink; - let suffix = relative_to_parent_of_target_dir(original)?; let ref dst = include_dir()?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); - symlink(path, dst)?; + symlink_or_copy(path, dst)?; let mut file_name = dst.file_name().unwrap().to_os_string(); file_name.push(".h"); - let dst2 = dst.with_file_name(file_name); - symlink(path, dst2)?; + let ref dst2 = dst.with_file_name(file_name); + symlink_or_copy(path, dst2)?; Ok(()) } @@ -102,3 +96,21 @@ fn canonicalize(path: impl AsRef) -> Result { // https://github.com/alexcrichton/cc-rs/issues/169 Ok(env::current_dir()?.join(path)) } + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_or_copy; + +#[cfg(windows)] +fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { + use std::os::windows::fs::symlink_file; + + // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they + // require Developer Mode. If it fails, fall back to copying the file. + if symlink_file(src, dst).is_err() { + fs::copy(src, dst)?; + } + Ok(()) +} + +#[cfg(not(any(unix, windows)))] +use std::fs::copy as symlink_or_copy; From f5dd552036cf28b7d7975c3bc37b0d8308a2500d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Apr 14 2020 21:13:48 +0000 Subject: [PATCH 268/2232] Adding &[u8] support. This change adds specifically, support for &[u8] with a corresponding rust::Slice type. No other types of slice are permitted. The rationale is that it may be common to pass binary data back and forth across the FFI boundary, so it's more urgent to get this in place sooner. Broader support for other slices can wait for the future. But, both C++ and Rust-side bindings should allow the existing support to be broadened to other Slice types in future without code changes. A few specific notes: * The name "rust::Slice" might be better as "rust::SliceRef" but I'm following the precedent of "rust::Str". * It would be good to add constructors from std::span but as that's a C++20 feature, that may have to wait until C++ feature detection is resolved. * Internally, this follows the pattern of &str, where the parser will initially recognize this as Type::Ref (of Type::Slice) but then will replace that with Type::SliceRefU8. Type::Slice should not persist through later stages. As we later come to support other types of slice, we would probably want to remove Type::SliceRefU8. --- diff --git a/README.md b/README.md index 0b3aab3..ad14b57 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ returns of functions. name in Rustname in C++restrictions Stringrust::String &strrust::Str +&[u8]rust::Slice<uint8_t>(no other slice types currently supported) CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type @@ -316,7 +317,6 @@ matter of designing a nice API for each in its non-native language. - diff --git a/gen/write.rs b/gen/write.rs index 4eed1b4..3f3cf4b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -365,6 +365,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, "::rust::Slice::Repr("), _ => {} } write!(out, "{}$(", efn.ident); @@ -395,7 +396,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) if !indirect_return => write!(out, ")"), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -566,6 +567,7 @@ fn write_rust_function_shim_impl( } match &arg.ty { Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} } @@ -573,7 +575,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) => write!(out, ")"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -637,6 +639,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } } @@ -645,7 +648,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} - Type::Str(_) => write!(out, " "), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), _ => write_space_after_type(out, ty), } } @@ -664,6 +667,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), + Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -676,6 +680,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -721,9 +726,16 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &r.inner); write!(out, " &"); } + Type::Slice(_) => { + // For now, only U8 slices are supported, which are covered separately below + unreachable!() + } Type::Str(_) => { write!(out, "::rust::Str"); } + Type::SliceRefU8(_) => { + write!(out, "::rust::Slice"); + } Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { @@ -750,11 +762,11 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::Fn(_) => { + Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) | Type::Fn(_) => { write!(out, " ") } Type::Ref(_) => {} - Type::Void(_) => unreachable!(), + Type::Void(_) | Type::Slice(_) => unreachable!(), } } diff --git a/include/cxx.h b/include/cxx.h index a8239a1..57a6e24 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -16,6 +16,43 @@ inline namespace cxxbridge02 { struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_SLICE +#define CXXBRIDGE02_RUST_SLICE +template +class Slice final { +public: + Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + Slice(const Slice &) noexcept = default; + + Slice(const T* s, size_t size) : repr(Repr{s, size}) {} + + Slice &operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; + } + + const T *data() const noexcept { return this->repr.ptr; } + size_t size() const noexcept { return this->repr.len; } + size_t length() const noexcept { return this->repr.len; } + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // At present this class is only used for &[u8] slices. + // Not necessarily ABI compatible with &[u8]. Codegen will translate to + // cxx::rust_slice_u8::RustSlice which matches this layout. + struct Repr { + const T *ptr; + size_t len; + }; + Slice(Repr repr_) noexcept : repr(repr_) {} + explicit operator Repr() noexcept { return this->repr; } + +private: + Repr repr; +}; + +#endif // CXXBRIDGE02_RUST_SLICE + #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING class String final { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 67c34e5..146d21b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -184,6 +184,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)), ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()), _ => quote!(#var), } @@ -255,6 +256,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), + Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))), _ => None, }) } else { @@ -267,6 +269,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), + Type::SliceRefU8(_) => Some(quote!(#call.as_slice())), _ => None, }) } @@ -375,6 +378,7 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), + Type::SliceRefU8(_) => quote!(#ident.as_slice()), ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), _ => quote!(#ident), } @@ -402,6 +406,7 @@ fn expand_rust_function_shim_impl( _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), + Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))), _ => None, }) .unwrap_or(call); @@ -572,6 +577,7 @@ fn expand_extern_type(ty: &Type) -> TokenStream { _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8), _ => quote!(#ty), } } diff --git a/src/lib.rs b/src/lib.rs index f918b29..60750fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,6 +367,7 @@ mod gen; mod opaque; mod paths; mod result; +mod rust_sliceu8; mod rust_str; mod rust_string; mod syntax; @@ -384,6 +385,7 @@ pub mod private { pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; + pub use crate::rust_sliceu8::RustSliceU8; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs new file mode 100644 index 0000000..56d37b3 --- /dev/null +++ b/src/rust_sliceu8.rs @@ -0,0 +1,26 @@ +use std::mem; +use std::slice; +use std::ptr::NonNull; + +// Not necessarily ABI compatible with &[u8]. Codegen performs the translation. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RustSliceU8 { + pub(crate) ptr: NonNull, + pub(crate) len: usize, +} + +impl RustSliceU8 { + pub fn from(s: &[u8]) -> Self { + RustSliceU8 { + ptr: NonNull::from(s).cast::(), + len: s.len(), + } + } + + pub unsafe fn as_slice<'a>(self) -> &'a [u8] { + slice::from_raw_parts(self.ptr.as_ptr(), self.len) + } +} + +const_assert!(mem::size_of::>() == mem::size_of::()); diff --git a/syntax/check.rs b/syntax/check.rs index 2826a85..c71295e 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -245,6 +245,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Slice(_) => "slice".to_owned(), + Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), } diff --git a/syntax/impls.rs b/syntax/impls.rs index 34fb851..b040753 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,4 @@ -use crate::syntax::{ExternFn, Receiver, Ref, Signature, Ty1, Type}; +use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -21,6 +21,8 @@ impl Hash for Type { Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::Fn(t) => t.hash(state), + Type::Slice(t) => t.hash(state), + Type::SliceRefU8(t) => t.hash(state), Type::Void(_) => {} } } @@ -37,6 +39,8 @@ impl PartialEq for Type { (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, + (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, + (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, (Type::Void(_), Type::Void(_)) => true, (_, _) => false, } @@ -106,6 +110,32 @@ impl Hash for Ref { } } +impl Eq for Slice {} + +impl PartialEq for Slice { + fn eq(&self, other: &Slice) -> bool { + let Slice { + bracket: _, + inner, + } = self; + let Slice { + bracket: _, + inner: inner2, + } = other; + inner == inner2 + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + let Slice { + bracket: _, + inner, + } = self; + inner.hash(state); + } +} + impl Eq for Signature {} impl PartialEq for Signature { diff --git a/syntax/mod.rs b/syntax/mod.rs index 0d0328b..4eaf81e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -15,7 +15,7 @@ pub mod types; use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; -use syn::token::{Brace, Paren}; +use syn::token::{Brace, Bracket, Paren}; use syn::{LitStr, Token}; pub use self::atom::Atom; @@ -84,6 +84,8 @@ pub enum Type { Str(Box), Fn(Box), Void(Span), + Slice(Box), + SliceRefU8(Box), } pub struct Ty1 { @@ -99,6 +101,11 @@ pub struct Ref { pub inner: Type, } +pub struct Slice { + pub bracket: Bracket, + pub inner: Type, +} + #[derive(Copy, Clone, PartialEq)] pub enum Lang { Cxx, diff --git a/syntax/parse.rs b/syntax/parse.rs index 8e674bd..6156a69 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,6 +1,6 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Struct, - Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, + Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::{format_ident, quote}; @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, - TypeBareFn, TypePath, TypeReference, + TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -222,10 +222,25 @@ fn parse_type(ty: &RustType) -> Result { RustType::Path(ty) => parse_type_path(ty), RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), + RustType::Slice(ty) => parse_type_slice(ty), _ => Err(Error::new_spanned(ty, "unsupported type")), } } +fn parse_type_slice(ty: &TypeSlice) -> Result { + let inner = parse_type(&ty.elem)?; + let which = match &inner { + Type::Ident(ident) if ident == "u8" => { + Type::Slice + }, + _ => return Err(Error::new_spanned(ty, "unsupported type")) + }; + Ok(which(Box::new(Slice { + bracket: ty.bracket_token, + inner + }))) +} + fn parse_type_reference(ty: &TypeReference) -> Result { let inner = parse_type(&ty.elem)?; let which = match &inner { @@ -236,6 +251,14 @@ fn parse_type_reference(ty: &TypeReference) -> Result { Type::Str } } + Type::Slice(inner2) => { + match &inner2.inner { + Type::Ident(ident) if ident == "u8" => { + Type::SliceRefU8 + } + _ => return Err(Error::new_spanned(ty, "unsupported type")) + } + } _ => Type::Ref, }; Ok(which(Box::new(Ref { diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 51592dd..df11852 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Ref, Signature, Slice, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::Token; @@ -15,7 +15,8 @@ impl ToTokens for Type { ident.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), - Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), + Type::Slice(s) => s.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } @@ -51,6 +52,14 @@ impl ToTokens for Ref { } } +impl ToTokens for Slice { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.bracket.surround(tokens, |tokens| { + self.inner.to_tokens(tokens); + }); + } +} + impl ToTokens for Derive { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { diff --git a/syntax/types.rs b/syntax/types.rs index f65b12f..6f3af09 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -23,9 +23,10 @@ impl<'a> Types<'a> { fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); match ty { - Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} + Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), + Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { if let Some(ret) = &f.ret { visit(all, ret); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 68651ea..adcd4fc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -24,6 +24,7 @@ pub mod ffi { fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_str(shared: &Shared) -> &str; + fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; @@ -34,6 +35,7 @@ pub mod ffi { fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); + fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); fn c_take_callback(callback: fn(String) -> usize); @@ -44,6 +46,7 @@ pub mod ffi { fn c_try_return_box() -> Result>; fn c_try_return_ref(s: &String) -> Result<&String>; fn c_try_return_str(s: &str) -> Result<&str>; + fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; } @@ -67,6 +70,7 @@ pub mod ffi { fn r_take_ref_r(r: &R); fn r_take_ref_c(c: &C); fn r_take_str(s: &str); + fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); @@ -160,6 +164,11 @@ fn r_take_rust_string(s: String) { assert_eq!(s, "2020"); } +fn r_take_sliceu8(s: &[u8]) { + assert_eq!(s.len(), 5); + assert_eq!(std::str::from_utf8(s).unwrap(), "2020\u{0}"); +} + fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4aad3a2..307b7e5 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -9,6 +9,8 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { +const char* SLICE_DATA = "2020"; + C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } @@ -32,6 +34,11 @@ rust::Str c_return_str(const Shared &shared) { return "2020"; } +rust::Slice c_return_sliceu8(const Shared& shared) { + (void)shared; + return rust::Slice((const unsigned char*)SLICE_DATA, 5); +} + rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { @@ -80,6 +87,12 @@ void c_take_str(rust::Str s) { } } +void c_take_sliceu8(rust::Slice s) { + if (std::string((const char*)s.data(), s.size()) == "2020") { + cxx_test_suite_set_correct(); + } +} + void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); @@ -108,6 +121,8 @@ const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } +rust::Slice c_try_return_sliceU8(rust::Slice s) { return s; } + rust::String c_try_return_rust_string() { return c_return_rust_string(); } std::unique_ptr c_try_return_unique_ptr_string() { @@ -146,6 +161,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); + r_take_sliceu8(rust::Slice((const unsigned char*)SLICE_DATA, 5)); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6713614..a68be5c 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,6 +23,7 @@ rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); rust::Str c_return_str(const Shared &shared); +rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); @@ -33,6 +34,7 @@ void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); void c_take_str(rust::Str s); +void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); void c_take_callback(rust::Fn callback); @@ -43,6 +45,7 @@ size_t c_fail_return_primitive(); rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); +rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); diff --git a/tests/test.rs b/tests/test.rs index f8e9ca8..ae2fc0c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -71,6 +71,7 @@ fn test_c_take() { check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); check!(ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); + check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string() From ec9430e6576655464b3835372d23f4a1e03f4bf0 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Apr 14 2020 23:12:21 +0000 Subject: [PATCH 269/2232] Adding missing tests; fixing typo. --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 307b7e5..bf5e76d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -121,7 +121,7 @@ const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } -rust::Slice c_try_return_sliceU8(rust::Slice s) { return s; } +rust::Slice c_try_return_sliceu8(rust::Slice s) { return s; } rust::String c_try_return_rust_string() { return c_return_rust_string(); } diff --git a/tests/test.rs b/tests/test.rs index ae2fc0c..511c52b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -29,6 +29,7 @@ fn test_c_return() { ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!("2020", ffi::c_return_str(&shared)); + assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!( "2020", @@ -51,6 +52,7 @@ fn test_c_try_return() { assert_eq!(2020, *ffi::c_try_return_box().unwrap()); assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); + assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap()); assert_eq!("2020", ffi::c_try_return_rust_string().unwrap()); assert_eq!( "2020", From f677f0aafa476757dc213a329a039917d6767ecb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:41:52 +0000 Subject: [PATCH 270/2232] Merge pull request #117 from adetaylor/sliceu8 Adding &[u8] support. --- diff --git a/README.md b/README.md index 0b3aab3..ad14b57 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ returns of functions. + @@ -316,7 +317,6 @@ matter of designing a nice API for each in its non-native language.
name in Rustname in C++
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[u8]rust::Slice<uint8_t>(no other slice types currently supported)
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
- diff --git a/gen/write.rs b/gen/write.rs index 4eed1b4..3f3cf4b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -365,6 +365,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, "::rust::Slice::Repr("), _ => {} } write!(out, "{}$(", efn.ident); @@ -395,7 +396,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) if !indirect_return => write!(out, ")"), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -566,6 +567,7 @@ fn write_rust_function_shim_impl( } match &arg.ty { Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} } @@ -573,7 +575,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) => write!(out, ")"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -637,6 +639,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } } @@ -645,7 +648,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} - Type::Str(_) => write!(out, " "), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), _ => write_space_after_type(out, ty), } } @@ -664,6 +667,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), + Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -676,6 +680,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } if types.needs_indirect_abi(&arg.ty) { @@ -721,9 +726,16 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &r.inner); write!(out, " &"); } + Type::Slice(_) => { + // For now, only U8 slices are supported, which are covered separately below + unreachable!() + } Type::Str(_) => { write!(out, "::rust::Str"); } + Type::SliceRefU8(_) => { + write!(out, "::rust::Slice"); + } Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { @@ -750,11 +762,11 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::Fn(_) => { + Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) | Type::Fn(_) => { write!(out, " ") } Type::Ref(_) => {} - Type::Void(_) => unreachable!(), + Type::Void(_) | Type::Slice(_) => unreachable!(), } } diff --git a/include/cxx.h b/include/cxx.h index a8239a1..57a6e24 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -16,6 +16,43 @@ inline namespace cxxbridge02 { struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_SLICE +#define CXXBRIDGE02_RUST_SLICE +template +class Slice final { +public: + Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + Slice(const Slice &) noexcept = default; + + Slice(const T* s, size_t size) : repr(Repr{s, size}) {} + + Slice &operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; + } + + const T *data() const noexcept { return this->repr.ptr; } + size_t size() const noexcept { return this->repr.len; } + size_t length() const noexcept { return this->repr.len; } + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // At present this class is only used for &[u8] slices. + // Not necessarily ABI compatible with &[u8]. Codegen will translate to + // cxx::rust_slice_u8::RustSlice which matches this layout. + struct Repr { + const T *ptr; + size_t len; + }; + Slice(Repr repr_) noexcept : repr(repr_) {} + explicit operator Repr() noexcept { return this->repr; } + +private: + Repr repr; +}; + +#endif // CXXBRIDGE02_RUST_SLICE + #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING class String final { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 67c34e5..146d21b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -184,6 +184,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)), ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()), _ => quote!(#var), } @@ -255,6 +256,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), + Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))), _ => None, }) } else { @@ -267,6 +269,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), + Type::SliceRefU8(_) => Some(quote!(#call.as_slice())), _ => None, }) } @@ -375,6 +378,7 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), + Type::SliceRefU8(_) => quote!(#ident.as_slice()), ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), _ => quote!(#ident), } @@ -402,6 +406,7 @@ fn expand_rust_function_shim_impl( _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), + Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))), _ => None, }) .unwrap_or(call); @@ -572,6 +577,7 @@ fn expand_extern_type(ty: &Type) -> TokenStream { _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), + Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8), _ => quote!(#ty), } } diff --git a/src/lib.rs b/src/lib.rs index 7ee2bfe..1ceef52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,6 +367,7 @@ mod gen; mod opaque; mod paths; mod result; +mod rust_sliceu8; mod rust_str; mod rust_string; mod syntax; @@ -384,6 +385,7 @@ pub mod private { pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; + pub use crate::rust_sliceu8::RustSliceU8; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs new file mode 100644 index 0000000..56d37b3 --- /dev/null +++ b/src/rust_sliceu8.rs @@ -0,0 +1,26 @@ +use std::mem; +use std::slice; +use std::ptr::NonNull; + +// Not necessarily ABI compatible with &[u8]. Codegen performs the translation. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RustSliceU8 { + pub(crate) ptr: NonNull, + pub(crate) len: usize, +} + +impl RustSliceU8 { + pub fn from(s: &[u8]) -> Self { + RustSliceU8 { + ptr: NonNull::from(s).cast::(), + len: s.len(), + } + } + + pub unsafe fn as_slice<'a>(self) -> &'a [u8] { + slice::from_raw_parts(self.ptr.as_ptr(), self.len) + } +} + +const_assert!(mem::size_of::>() == mem::size_of::()); diff --git a/syntax/check.rs b/syntax/check.rs index 2826a85..c71295e 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -245,6 +245,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Slice(_) => "slice".to_owned(), + Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), Type::Void(_) => "()".to_owned(), } diff --git a/syntax/impls.rs b/syntax/impls.rs index 34fb851..b040753 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,4 @@ -use crate::syntax::{ExternFn, Receiver, Ref, Signature, Ty1, Type}; +use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -21,6 +21,8 @@ impl Hash for Type { Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::Fn(t) => t.hash(state), + Type::Slice(t) => t.hash(state), + Type::SliceRefU8(t) => t.hash(state), Type::Void(_) => {} } } @@ -37,6 +39,8 @@ impl PartialEq for Type { (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs, (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, + (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, + (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, (Type::Void(_), Type::Void(_)) => true, (_, _) => false, } @@ -106,6 +110,32 @@ impl Hash for Ref { } } +impl Eq for Slice {} + +impl PartialEq for Slice { + fn eq(&self, other: &Slice) -> bool { + let Slice { + bracket: _, + inner, + } = self; + let Slice { + bracket: _, + inner: inner2, + } = other; + inner == inner2 + } +} + +impl Hash for Slice { + fn hash(&self, state: &mut H) { + let Slice { + bracket: _, + inner, + } = self; + inner.hash(state); + } +} + impl Eq for Signature {} impl PartialEq for Signature { diff --git a/syntax/mod.rs b/syntax/mod.rs index 0d0328b..4eaf81e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -15,7 +15,7 @@ pub mod types; use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; -use syn::token::{Brace, Paren}; +use syn::token::{Brace, Bracket, Paren}; use syn::{LitStr, Token}; pub use self::atom::Atom; @@ -84,6 +84,8 @@ pub enum Type { Str(Box), Fn(Box), Void(Span), + Slice(Box), + SliceRefU8(Box), } pub struct Ty1 { @@ -99,6 +101,11 @@ pub struct Ref { pub inner: Type, } +pub struct Slice { + pub bracket: Bracket, + pub inner: Type, +} + #[derive(Copy, Clone, PartialEq)] pub enum Lang { Cxx, diff --git a/syntax/parse.rs b/syntax/parse.rs index 8e674bd..6156a69 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,6 +1,6 @@ use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Struct, - Ty1, Type, Var, + attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, + Struct, Ty1, Type, Var, }; use proc_macro2::Ident; use quote::{format_ident, quote}; @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::{ Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, - TypeBareFn, TypePath, TypeReference, + TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -222,10 +222,25 @@ fn parse_type(ty: &RustType) -> Result { RustType::Path(ty) => parse_type_path(ty), RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), + RustType::Slice(ty) => parse_type_slice(ty), _ => Err(Error::new_spanned(ty, "unsupported type")), } } +fn parse_type_slice(ty: &TypeSlice) -> Result { + let inner = parse_type(&ty.elem)?; + let which = match &inner { + Type::Ident(ident) if ident == "u8" => { + Type::Slice + }, + _ => return Err(Error::new_spanned(ty, "unsupported type")) + }; + Ok(which(Box::new(Slice { + bracket: ty.bracket_token, + inner + }))) +} + fn parse_type_reference(ty: &TypeReference) -> Result { let inner = parse_type(&ty.elem)?; let which = match &inner { @@ -236,6 +251,14 @@ fn parse_type_reference(ty: &TypeReference) -> Result { Type::Str } } + Type::Slice(inner2) => { + match &inner2.inner { + Type::Ident(ident) if ident == "u8" => { + Type::SliceRefU8 + } + _ => return Err(Error::new_spanned(ty, "unsupported type")) + } + } _ => Type::Ref, }; Ok(which(Box::new(Ref { diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 51592dd..df11852 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Ref, Signature, Slice, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::Token; @@ -15,7 +15,8 @@ impl ToTokens for Type { ident.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), - Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens), + Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), + Type::Slice(s) => s.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())), } @@ -51,6 +52,14 @@ impl ToTokens for Ref { } } +impl ToTokens for Slice { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.bracket.surround(tokens, |tokens| { + self.inner.to_tokens(tokens); + }); + } +} + impl ToTokens for Derive { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { diff --git a/syntax/types.rs b/syntax/types.rs index f65b12f..6f3af09 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -23,9 +23,10 @@ impl<'a> Types<'a> { fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); match ty { - Type::Ident(_) | Type::Str(_) | Type::Void(_) => {} + Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), + Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { if let Some(ret) = &f.ret { visit(all, ret); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 68651ea..adcd4fc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -24,6 +24,7 @@ pub mod ffi { fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_str(shared: &Shared) -> &str; + fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; @@ -34,6 +35,7 @@ pub mod ffi { fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); + fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); fn c_take_callback(callback: fn(String) -> usize); @@ -44,6 +46,7 @@ pub mod ffi { fn c_try_return_box() -> Result>; fn c_try_return_ref(s: &String) -> Result<&String>; fn c_try_return_str(s: &str) -> Result<&str>; + fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; } @@ -67,6 +70,7 @@ pub mod ffi { fn r_take_ref_r(r: &R); fn r_take_ref_c(c: &C); fn r_take_str(s: &str); + fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); @@ -160,6 +164,11 @@ fn r_take_rust_string(s: String) { assert_eq!(s, "2020"); } +fn r_take_sliceu8(s: &[u8]) { + assert_eq!(s.len(), 5); + assert_eq!(std::str::from_utf8(s).unwrap(), "2020\u{0}"); +} + fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4aad3a2..bf5e76d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -9,6 +9,8 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { +const char* SLICE_DATA = "2020"; + C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } @@ -32,6 +34,11 @@ rust::Str c_return_str(const Shared &shared) { return "2020"; } +rust::Slice c_return_sliceu8(const Shared& shared) { + (void)shared; + return rust::Slice((const unsigned char*)SLICE_DATA, 5); +} + rust::String c_return_rust_string() { return "2020"; } std::unique_ptr c_return_unique_ptr_string() { @@ -80,6 +87,12 @@ void c_take_str(rust::Str s) { } } +void c_take_sliceu8(rust::Slice s) { + if (std::string((const char*)s.data(), s.size()) == "2020") { + cxx_test_suite_set_correct(); + } +} + void c_take_rust_string(rust::String s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); @@ -108,6 +121,8 @@ const rust::String &c_try_return_ref(const rust::String &s) { return s; } rust::Str c_try_return_str(rust::Str s) { return s; } +rust::Slice c_try_return_sliceu8(rust::Slice s) { return s; } + rust::String c_try_return_rust_string() { return c_return_rust_string(); } std::unique_ptr c_try_return_unique_ptr_string() { @@ -146,6 +161,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); + r_take_sliceu8(rust::Slice((const unsigned char*)SLICE_DATA, 5)); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6713614..a68be5c 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,6 +23,7 @@ rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); rust::Str c_return_str(const Shared &shared); +rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); @@ -33,6 +34,7 @@ void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); void c_take_str(rust::Str s); +void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); void c_take_callback(rust::Fn callback); @@ -43,6 +45,7 @@ size_t c_fail_return_primitive(); rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); +rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); diff --git a/tests/test.rs b/tests/test.rs index f8e9ca8..511c52b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -29,6 +29,7 @@ fn test_c_return() { ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!("2020", ffi::c_return_str(&shared)); + assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!( "2020", @@ -51,6 +52,7 @@ fn test_c_try_return() { assert_eq!(2020, *ffi::c_try_return_box().unwrap()); assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); + assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap()); assert_eq!("2020", ffi::c_try_return_rust_string().unwrap()); assert_eq!( "2020", @@ -71,6 +73,7 @@ fn test_c_take() { check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); check!(ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); + check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string() From eb952bac142ac4755d8fd2c854c9fbc1d697d557 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 271/2232] Format with rustfmt and clang-format --- diff --git a/gen/write.rs b/gen/write.rs index 3f3cf4b..f930d52 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -365,7 +365,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), - Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, "::rust::Slice::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => { + write!(out, "::rust::Slice::Repr(") + } _ => {} } write!(out, "{}$(", efn.ident); @@ -762,9 +764,12 @@ fn write_type_space(out: &mut OutFile, ty: &Type) { fn write_space_after_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) | Type::Fn(_) => { - write!(out, " ") - } + Type::Ident(_) + | Type::RustBox(_) + | Type::UniquePtr(_) + | Type::Str(_) + | Type::SliceRefU8(_) + | Type::Fn(_) => write!(out, " "), Type::Ref(_) => {} Type::Void(_) | Type::Slice(_) => unreachable!(), } diff --git a/include/cxx.h b/include/cxx.h index 57a6e24..e63790b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -18,13 +18,13 @@ struct unsafe_bitcopy_t; #ifndef CXXBRIDGE02_RUST_SLICE #define CXXBRIDGE02_RUST_SLICE -template +template class Slice final { public: Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Slice(const Slice &) noexcept = default; - Slice(const T* s, size_t size) : repr(Repr{s, size}) {} + Slice(const T *s, size_t size) : repr(Repr{s, size}) {} Slice &operator=(Slice other) noexcept { this->repr = other.repr; diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index 56d37b3..a0c348b 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -1,6 +1,6 @@ use std::mem; -use std::slice; use std::ptr::NonNull; +use std::slice; // Not necessarily ABI compatible with &[u8]. Codegen performs the translation. #[repr(C)] diff --git a/syntax/impls.rs b/syntax/impls.rs index b040753..d3c4b0f 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -114,10 +114,7 @@ impl Eq for Slice {} impl PartialEq for Slice { fn eq(&self, other: &Slice) -> bool { - let Slice { - bracket: _, - inner, - } = self; + let Slice { bracket: _, inner } = self; let Slice { bracket: _, inner: inner2, @@ -128,10 +125,7 @@ impl PartialEq for Slice { impl Hash for Slice { fn hash(&self, state: &mut H) { - let Slice { - bracket: _, - inner, - } = self; + let Slice { bracket: _, inner } = self; inner.hash(state); } } diff --git a/syntax/parse.rs b/syntax/parse.rs index 6156a69..95dd67c 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -230,14 +230,12 @@ fn parse_type(ty: &RustType) -> Result { fn parse_type_slice(ty: &TypeSlice) -> Result { let inner = parse_type(&ty.elem)?; let which = match &inner { - Type::Ident(ident) if ident == "u8" => { - Type::Slice - }, - _ => return Err(Error::new_spanned(ty, "unsupported type")) + Type::Ident(ident) if ident == "u8" => Type::Slice, + _ => return Err(Error::new_spanned(ty, "unsupported type")), }; Ok(which(Box::new(Slice { bracket: ty.bracket_token, - inner + inner, }))) } @@ -251,14 +249,10 @@ fn parse_type_reference(ty: &TypeReference) -> Result { Type::Str } } - Type::Slice(inner2) => { - match &inner2.inner { - Type::Ident(ident) if ident == "u8" => { - Type::SliceRefU8 - } - _ => return Err(Error::new_spanned(ty, "unsupported type")) - } - } + Type::Slice(inner2) => match &inner2.inner { + Type::Ident(ident) if ident == "u8" => Type::SliceRefU8, + _ => return Err(Error::new_spanned(ty, "unsupported type")), + }, _ => Type::Ref, }; Ok(which(Box::new(Ref { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index bf5e76d..8b89ae8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -9,7 +9,7 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { -const char* SLICE_DATA = "2020"; +const char *SLICE_DATA = "2020"; C::C(size_t n) : n(n) {} @@ -34,9 +34,9 @@ rust::Str c_return_str(const Shared &shared) { return "2020"; } -rust::Slice c_return_sliceu8(const Shared& shared) { +rust::Slice c_return_sliceu8(const Shared &shared) { (void)shared; - return rust::Slice((const unsigned char*)SLICE_DATA, 5); + return rust::Slice((const unsigned char *)SLICE_DATA, 5); } rust::String c_return_rust_string() { return "2020"; } @@ -88,7 +88,7 @@ void c_take_str(rust::Str s) { } void c_take_sliceu8(rust::Slice s) { - if (std::string((const char*)s.data(), s.size()) == "2020") { + if (std::string((const char *)s.data(), s.size()) == "2020") { cxx_test_suite_set_correct(); } } @@ -161,7 +161,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); - r_take_sliceu8(rust::Slice((const unsigned char*)SLICE_DATA, 5)); + r_take_sliceu8(rust::Slice((const unsigned char *)SLICE_DATA, 5)); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); From efe81052e75d3d401d3708e403a3d569c5c4df5b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 272/2232] Touch up &[u8] PR --- diff --git a/README.md b/README.md index ad14b57..f51a1ac 100644 --- a/README.md +++ b/README.md @@ -299,7 +299,7 @@ returns of functions. - + diff --git a/include/cxx.h b/include/cxx.h index e63790b..cf79cfb 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -16,43 +16,6 @@ inline namespace cxxbridge02 { struct unsafe_bitcopy_t; -#ifndef CXXBRIDGE02_RUST_SLICE -#define CXXBRIDGE02_RUST_SLICE -template -class Slice final { -public: - Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} - Slice(const Slice &) noexcept = default; - - Slice(const T *s, size_t size) : repr(Repr{s, size}) {} - - Slice &operator=(Slice other) noexcept { - this->repr = other.repr; - return *this; - } - - const T *data() const noexcept { return this->repr.ptr; } - size_t size() const noexcept { return this->repr.len; } - size_t length() const noexcept { return this->repr.len; } - - // Repr is PRIVATE; must not be used other than by our generated code. - // - // At present this class is only used for &[u8] slices. - // Not necessarily ABI compatible with &[u8]. Codegen will translate to - // cxx::rust_slice_u8::RustSlice which matches this layout. - struct Repr { - const T *ptr; - size_t len; - }; - Slice(Repr repr_) noexcept : repr(repr_) {} - explicit operator Repr() noexcept { return this->repr; } - -private: - Repr repr; -}; - -#endif // CXXBRIDGE02_RUST_SLICE - #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING class String final { @@ -120,6 +83,42 @@ private: }; #endif // CXXBRIDGE02_RUST_STR +#ifndef CXXBRIDGE02_RUST_SLICE +#define CXXBRIDGE02_RUST_SLICE +template +class Slice final { +public: + Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + Slice(const Slice &) noexcept = default; + + Slice(const T *s, size_t size) : repr(Repr{s, size}) {} + + Slice &operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; + } + + const T *data() const noexcept { return this->repr.ptr; } + size_t size() const noexcept { return this->repr.len; } + size_t length() const noexcept { return this->repr.len; } + + // Repr is PRIVATE; must not be used other than by our generated code. + // + // At present this class is only used for &[u8] slices. + // Not necessarily ABI compatible with &[u8]. Codegen will translate to + // cxx::rust_slice_u8::RustSlice which matches this layout. + struct Repr { + const T *ptr; + size_t len; + }; + Slice(Repr repr_) noexcept : repr(repr_) {} + explicit operator Repr() noexcept { return this->repr; } + +private: + Repr repr; +}; +#endif // CXXBRIDGE02_RUST_SLICE + #ifndef CXXBRIDGE02_RUST_BOX #define CXXBRIDGE02_RUST_BOX template diff --git a/src/lib.rs b/src/lib.rs index 1ceef52..6348cf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -307,6 +307,7 @@ //! //! //! +//! //! //! //! @@ -324,7 +325,6 @@ //! //!
name in Rustname in C++
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[u8]rust::Slice<uint8_t>(no other slice types currently supported)
&[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
//! -//! //! //! //! diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index adcd4fc..8afa841 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -166,7 +166,7 @@ fn r_take_rust_string(s: String) { fn r_take_sliceu8(s: &[u8]) { assert_eq!(s.len(), 5); - assert_eq!(std::str::from_utf8(s).unwrap(), "2020\u{0}"); + assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0"); } fn r_take_unique_ptr_string(s: UniquePtr) { From e710af1dd78575894621dcf628ba3ca9763e318f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 273/2232] Fix typo in Slice repr comment --- diff --git a/include/cxx.h b/include/cxx.h index cf79cfb..637372a 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -106,7 +106,7 @@ public: // // At present this class is only used for &[u8] slices. // Not necessarily ABI compatible with &[u8]. Codegen will translate to - // cxx::rust_slice_u8::RustSlice which matches this layout. + // cxx::rust_sliceu8::RustSliceU8 which matches this layout. struct Repr { const T *ptr; size_t len; From eebe9b7289d1b01da10fbb4ea5e698bddb9c4ce1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 274/2232] Improve type checking and error messages for slice type --- diff --git a/syntax/check.rs b/syntax/check.rs index c71295e..181c530 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Struct, Ty1, Type, Types}; +use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Slice, Struct, Ty1, Type, Types}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; @@ -29,6 +29,7 @@ fn do_typecheck(cx: &mut Check) { Type::RustBox(ptr) => check_type_box(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), + Type::Slice(ty) => check_type_slice(cx, ty), _ => {} } } @@ -107,6 +108,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) { cx.error(ty, "unsupported reference type"); } +fn check_type_slice(cx: &mut Check, ty: &Slice) { + cx.error(ty, "only &[u8] is supported so far, not other slice types"); +} + fn check_api_struct(cx: &mut Check, strct: &Struct) { if strct.fields.is_empty() { let span = span_for_struct_error(strct); @@ -201,7 +206,7 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, - Type::Void(_) => return true, + Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) diff --git a/syntax/parse.rs b/syntax/parse.rs index 95dd67c..6e5b36e 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,3 +1,4 @@ +use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, Var, @@ -220,25 +221,13 @@ fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), RustType::Path(ty) => parse_type_path(ty), + RustType::Slice(ty) => parse_type_slice(ty), RustType::BareFn(ty) => parse_type_fn(ty), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), - RustType::Slice(ty) => parse_type_slice(ty), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_slice(ty: &TypeSlice) -> Result { - let inner = parse_type(&ty.elem)?; - let which = match &inner { - Type::Ident(ident) if ident == "u8" => Type::Slice, - _ => return Err(Error::new_spanned(ty, "unsupported type")), - }; - Ok(which(Box::new(Slice { - bracket: ty.bracket_token, - inner, - }))) -} - fn parse_type_reference(ty: &TypeReference) -> Result { let inner = parse_type(&ty.elem)?; let which = match &inner { @@ -249,9 +238,9 @@ fn parse_type_reference(ty: &TypeReference) -> Result { Type::Str } } - Type::Slice(inner2) => match &inner2.inner { - Type::Ident(ident) if ident == "u8" => Type::SliceRefU8, - _ => return Err(Error::new_spanned(ty, "unsupported type")), + Type::Slice(slice) => match &slice.inner { + Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8, + _ => Type::Ref, }, _ => Type::Ref, }; @@ -298,6 +287,14 @@ fn parse_type_path(ty: &TypePath) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } +fn parse_type_slice(ty: &TypeSlice) -> Result { + let inner = parse_type(&ty.elem)?; + Ok(Type::Slice(Box::new(Slice { + bracket: ty.bracket_token, + inner, + }))) +} + fn parse_type_fn(ty: &TypeBareFn) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( From 4770b4783b6ec408a6d1b97bf5c685b1874fa038 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 275/2232] Include the right headers when using a slice --- diff --git a/gen/write.rs b/gen/write.rs index f930d52..322ca08 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -100,6 +100,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, + Type::SliceRefU8(_) => out.include.cstdint = true, _ => {} } } @@ -108,6 +109,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_string = false; let mut needs_rust_str = false; + let mut needs_rust_slice = false; let mut needs_rust_box = false; let mut needs_rust_fn = false; let mut needs_rust_isize = false; @@ -125,6 +127,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Type::Fn(_) => { needs_rust_fn = true; } + Type::Slice(_) | Type::SliceRefU8(_) => { + needs_rust_slice = true; + } ty if ty == Isize => { out.include.base_tsd = true; needs_rust_isize = true; @@ -183,6 +188,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { if needs_rust_string || needs_rust_str + || needs_rust_slice || needs_rust_box || needs_rust_fn || needs_rust_error @@ -202,6 +208,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); + write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); From 633b1f5f1f65f730885e358f5063a8b589ebdb71 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:42:06 +0000 Subject: [PATCH 276/2232] Switch C-style casting to reinterpret_casts --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8b89ae8..d72bfd0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -36,7 +36,7 @@ rust::Str c_return_str(const Shared &shared) { rust::Slice c_return_sliceu8(const Shared &shared) { (void)shared; - return rust::Slice((const unsigned char *)SLICE_DATA, 5); + return rust::Slice(reinterpret_cast(SLICE_DATA), 5); } rust::String c_return_rust_string() { return "2020"; } @@ -88,7 +88,8 @@ void c_take_str(rust::Str s) { } void c_take_sliceu8(rust::Slice s) { - if (std::string((const char *)s.data(), s.size()) == "2020") { + if (std::string(reinterpret_cast(s.data()), s.size()) == + "2020") { cxx_test_suite_set_correct(); } } @@ -161,7 +162,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); - r_take_sliceu8(rust::Slice((const unsigned char *)SLICE_DATA, 5)); + r_take_sliceu8( + rust::Slice(reinterpret_cast(SLICE_DATA), 5)); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); From 4272d98530c9813d36c26ea626efc20a75d1055b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 14 2020 23:57:00 +0000 Subject: [PATCH 277/2232] Release 0.2.9 --- diff --git a/Cargo.toml b/Cargo.toml index 044095b..f9d9b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.8" # remember to update html_root_url +version = "0.2.9" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.8", path = "macro" } +cxxbridge-macro = { version = "=0.2.9", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 4a4cde2..7c0739b 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.8" +version = "0.2.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index c85062f..386c9dd 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.8" +version = "0.2.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 6348cf4..609db59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.8")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.9")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index fce3ce0..71ad826 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.8" +version = "0.2.9" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.8" +version = "0.2.9" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.8" +version = "0.2.9" dependencies = [ "cxx", "proc-macro2", From 3d4f612b348607bb19d0f2013e383485eb18bb2c Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 16 2020 23:24:48 +0000 Subject: [PATCH 278/2232] Support calling C++ methods from Rust These methods can be declared in the bridge by naming the first argument self and making it a reference to the containing class, e.g., fn get(self: &C) -> usize; fn set(self: &mut C, n: usize); This syntax requires Rust 1.43. Note that the implementation also changes the internal naming of shim functions so that they also contain the name of the owning class, if any. This allows defining multiple methods with the same name on different objects. --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..6fc2465 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,12 +9,15 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } +const std::string &ThingC::get_name() const { + std::cout << "I'm a C++ method!" << std::endl; + return this->appname; +} + std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } -const std::string &get_name(const ThingC &thing) { return thing.appname; } - void do_thing(SharedThing state) { print_r(*state.y); } } // namespace example diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index fafc474..885293f 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -12,12 +12,13 @@ public: ~ThingC(); std::string appname; + + const std::string &get_name() const; }; struct SharedThing; std::unique_ptr make_demo(rust::Str appname); -const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); } // namespace example diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 66dfc79..759bbe1 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -11,8 +11,9 @@ mod ffi { type ThingC; fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; + fn get_name(self: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); + } extern "Rust" { @@ -29,7 +30,7 @@ fn print_r(r: &ThingR) { fn main() { let x = ffi::make_demo("demo of cxx::bridge"); - println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); + println!("this is a {}", x.as_ref().unwrap().get_name()); ffi::do_thing(ffi::SharedThing { z: 222, diff --git a/gen/write.rs b/gen/write.rs index 322ca08..41b5b37 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,7 @@ use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{Api, ExternFn, Signature, Struct, Type, Types, Var}; +use crate::syntax::{Api, ExternFn, Receiver, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; pub(super) fn gen( @@ -327,8 +327,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_extern_return_type_space(out, &efn.ret, types); } write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); + if let Some(base) = &efn.receiver { + write!(out, "{} *__receiver$", base.ident); + } for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { + if i > 0 || efn.receiver.is_some() { write!(out, ", "); } if arg.ty == RustString { @@ -347,14 +350,27 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ") noexcept {{"); write!(out, " "); write_return_type(out, &efn.ret); - write!(out, "(*{}$)(", efn.ident); + match &efn.receiver { + None => write!(out, "(*{}$)(", efn.ident), + Some(base) => write!(out, "({}::*{}$)(", base.ident, efn.ident), + } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type(out, &arg.ty); } - writeln!(out, ") = {};", efn.ident); + write!(out, ")"); + match &efn.receiver { + Some(Receiver { mutability: None, ident: _ }) => write!(out, " const"), + _ => {}, + } + write!(out, " = "); + match &efn.receiver { + None => write!(out, "{}", efn.ident), + Some(base) => write!(out, "&{}::{}", base.ident, efn.ident), + } + writeln!(out, ";"); write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); @@ -377,7 +393,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } _ => {} } - write!(out, "{}$(", efn.ident); + match &efn.receiver { + None => write!(out, "{}$(", efn.ident), + Some(_) => write!(out, "(__receiver$->*{}$)(", efn.ident), + } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 146d21b..998b47e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -123,6 +123,13 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; + let receiver = efn.receiver.iter().map(|base| { + let ident = &base.ident; + match base.mutability { + None => quote!(_: &#ident), + Some(_) => quote!(_: &mut #ident), + } + }); let args = efn.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -136,6 +143,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types quote!(#ident: #ty) } }); + let all_args = receiver.chain(args); let ret = if efn.throws { quote!(-> ::cxx::private::Result) } else { @@ -150,7 +158,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] - fn #local_name(#(#args,)* #outparam) #ret; + fn #local_name(#(#all_args,)* #outparam) #ret; } } @@ -158,7 +166,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); - let args = &efn.args; + let receiver = efn.receiver.iter().map(|base| match base.mutability { + None => quote!(&self), + Some(_) => quote!(&mut self), + }); + let args = efn.args.iter().map(|arg| quote!(#arg)); + let all_args = receiver.chain(args); let ret = if efn.throws { let ok = match &efn.ret { Some(ret) => quote!(#ret), @@ -169,7 +182,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types expand_return_type(&efn.ret) }; let indirect_return = indirect_return(efn, types); - let vars = efn.args.iter().map(|arg| { + let receiver_var = efn.receiver.iter().map(|_| quote!(self)); + let arg_vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { Type::Ident(ident) if ident == RustString => { @@ -189,6 +203,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), } }); + let vars = receiver_var.chain(arg_vars); let trampolines = efn .args .iter() @@ -274,18 +289,36 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } .unwrap_or(call); - quote! { - #doc - pub fn #ident(#args) #ret { - extern "C" { - #decl + let receiver_ident = efn.receiver.as_ref().map(|base| &base.ident); + match receiver_ident { + None => quote! { + #doc + pub fn #ident(#(#all_args,)*) #ret { + extern "C" { + #decl + } + #trampolines + unsafe { + #setup + #expr + } } - #trampolines - unsafe { - #setup - #expr + }, + Some(base_ident) => quote! { + #doc + impl #base_ident { + pub fn #ident(#(#all_args,)*) #ret { + extern "C" { + #decl + } + #trampolines + unsafe { + #setup + #expr + } + } } - } + }, } } diff --git a/syntax/check.rs b/syntax/check.rs index 181c530..3b6937a 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -195,6 +195,10 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } + if efn.receiver.is_some() { + reference_args += 1; + } + if reference_args != 1 { cx.error( efn, diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 8afa841..a720a80 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -49,6 +49,9 @@ pub mod ffi { fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; + + fn get(self: &C) -> usize; + fn set(self: &mut C, n: usize) -> usize; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d72bfd0..2daae53 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -15,6 +15,11 @@ C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } +size_t C::set(size_t n) { + this->n = n; + return this->n; +} + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index a68be5c..43ac229 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -12,6 +12,7 @@ class C { public: C(size_t n); size_t get() const; + size_t set(size_t n); private: size_t n; diff --git a/tests/test.rs b/tests/test.rs index 511c52b..36de8f9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -107,6 +107,18 @@ fn test_c_call_r() { check!(cxx_run_test()); } +#[test] +fn test_c_method_calls() { + let mut unique_ptr = ffi::c_return_unique_ptr(); + + let old_value = unique_ptr.as_ref().unwrap().get(); + assert_eq!(2020, old_value); + assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021)); + assert_eq!(2021, unique_ptr.as_ref().unwrap().get()); + assert_eq!(old_value, unique_ptr.as_mut().unwrap().set(old_value)); + assert_eq!(old_value, unique_ptr.as_ref().unwrap().get()) +} + #[no_mangle] extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { Box::into_raw(Box::new(2020usize)) From c1c4e7ac6b92ad33f134882f848a6ad034ef3de2 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 16 2020 23:25:04 +0000 Subject: [PATCH 279/2232] Support calling Rust methods from C++ These methods can be declared in the bridge by naming the first argument self and making it a reference to the containing class, e.g., fn get(self: &R) -> usize; fn set(self: &mut R, n: usize); This syntax requires Rust 1.43. --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 6fc2465..bc0d976 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -18,7 +18,10 @@ std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } -void do_thing(SharedThing state) { print_r(*state.y); } +void do_thing(SharedThing state) { + print_r(*state.y); + state.y->print(); +} } // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 759bbe1..713a1d1 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -19,6 +19,7 @@ mod ffi { extern "Rust" { type ThingR; fn print_r(r: &ThingR); + fn print(self: &ThingR); } } @@ -28,6 +29,12 @@ fn print_r(r: &ThingR) { println!("called back with r={}", r.0); } +impl ThingR { + fn print(&self) { + println!("method called back with r={}", self.0); + } +} + fn main() { let x = ffi::make_demo("demo of cxx::bridge"); println!("this is a {}", x.as_ref().unwrap().get_name()); diff --git a/gen/write.rs b/gen/write.rs index 41b5b37..7e1caac 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,7 @@ use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{Api, ExternFn, Receiver, Signature, Struct, Type, Types, Var}; +use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; pub(super) fn gen( @@ -45,9 +45,25 @@ pub(super) fn gen( } for api in apis { - if let Api::Struct(strct) = api { - out.next_section(); - write_struct(out, strct); + match api { + Api::Struct(strct) => { + out.next_section(); + write_struct(out, strct); + } + Api::RustType(ety) => { + let methods = apis.iter().filter_map(|api| match api { + Api::RustFunction(efn) => match &efn.sig.receiver { + Some(rcvr) if rcvr.ident == ety.ident => Some(efn), + _ => None, + }, + _ => None, + }).collect::>(); + if !methods.is_empty() { + out.next_section(); + write_struct_with_methods(out, ety, methods); + } + } + _ => {} } } @@ -300,6 +316,21 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: Vec<&ExternFn>) { + for line in ety.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", ety.ident); + for method in &methods { + write!(out, " "); + let sig = &method.sig; + let local_name = method.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, None, false); + writeln!(out, ";"); + } + writeln!(out, "}};"); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { @@ -326,7 +357,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + write!(out, "{}cxxbridge02${}${}(", out.namespace, receiver_type, efn.ident); if let Some(base) = &efn.receiver { write!(out, "{} *__receiver$", base.ident); } @@ -471,7 +506,11 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let link_name = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -490,6 +529,10 @@ fn write_rust_function_decl_impl( } write!(out, "{}(", link_name); let mut needs_comma = false; + if let Some(base) = &sig.receiver { + write!(out, "{} &__receiver$", base.ident); + needs_comma = true; + } for arg in &sig.args { if needs_comma { write!(out, ", "); @@ -519,20 +562,26 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = efn.ident.to_string(); - let invoke = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let invoke = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } -fn write_rust_function_shim_impl( +fn write_rust_function_shim_decl( out: &mut OutFile, local_name: &str, sig: &Signature, - types: &Types, - invoke: &str, + receiver: Option<&Receiver>, indirect_call: bool, ) { write_return_type(out, &sig.ret); + if let Some(base) = receiver { + write!(out, "{}::", base.ident); + } write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { @@ -551,6 +600,21 @@ fn write_rust_function_shim_impl( if !sig.throws { write!(out, " noexcept"); } +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &str, + indirect_call: bool, +) { + if out.header && sig.receiver.is_some() { + // We've already defined this inside the struct. + return; + } + write_rust_function_shim_decl(out, local_name, sig, sig.receiver.as_ref(), indirect_call); if out.header { writeln!(out, ";"); } else { @@ -589,8 +653,11 @@ fn write_rust_function_shim_impl( write!(out, "::rust::Str::Repr error$ = "); } write!(out, "{}(", invoke); + if let Some(_) = &sig.receiver { + write!(out, "*this"); + } for (i, arg) in sig.args.iter().enumerate() { - if i > 0 { + if i > 0 || sig.receiver.is_some() { write!(out, ", "); } match &arg.ty { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 998b47e..2d3166a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -154,7 +154,11 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = format!("{}cxxbridge02${}", namespace, ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] @@ -366,7 +370,11 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let link_name = format!("{}cxxbridge02${}", namespace, ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); let invoke = Some(ident); @@ -388,6 +396,13 @@ fn expand_rust_function_shim_impl( catch_unwind_label: String, invoke: Option<&Ident>, ) -> TokenStream { + let receiver = sig.receiver.iter().map(|base| { + let ident = &base.ident; + match base.mutability { + None => quote!(__receiver: &#ident), + Some(_) => quote!(__receiver: &mut #ident), + } + }); let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -397,6 +412,7 @@ fn expand_rust_function_shim_impl( quote!(#ident: #ty) } }); + let all_args = receiver.chain(args); let vars = sig.args.iter().map(|arg| { let ident = &arg.ident; @@ -418,7 +434,10 @@ fn expand_rust_function_shim_impl( }); let mut call = match invoke { - Some(ident) => quote!(super::#ident), + Some(ident) => match sig.receiver { + None => quote!(super::#ident), + Some(_) => quote!(__receiver.#ident), + }, None => quote!(__extern), }; call.extend(quote! { (#(#vars),*) }); @@ -476,7 +495,7 @@ fn expand_rust_function_shim_impl( quote! { #[doc(hidden)] #[export_name = #link_name] - unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret { + unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret { let __fn = concat!(module_path!(), #catch_unwind_label); #expr } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index a720a80..4da5740 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -56,6 +56,7 @@ pub mod ffi { extern "Rust" { type R; + type R2; fn r_return_primitive() -> usize; fn r_return_shared() -> Shared; @@ -80,11 +81,28 @@ pub mod ffi { fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; fn r_fail_return_primitive() -> Result; + + fn r_return_r2(n: usize) -> Box; + fn get(self: &R2) -> usize; + fn set(self: &mut R2, n: usize) -> usize; } } pub type R = usize; +pub struct R2(usize); + +impl R2 { + fn get(&self) -> usize { + self.0 + } + + fn set(&mut self, n: usize) -> usize { + self.0 = n; + n + } +} + #[derive(Debug)] struct Error; @@ -187,3 +205,7 @@ fn r_try_return_primitive() -> Result { fn r_fail_return_primitive() -> Result { Err(Error) } + +fn r_return_r2(n: usize) -> Box { + Box::new(R2(n)) +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2daae53..8893ee8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -181,6 +181,13 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(std::strcmp(e.what(), "rust error") == 0); } + auto r2 = r_return_r2(2020); + ASSERT(r2->get() == 2020); + ASSERT(r2->set(2021) == 2021); + ASSERT(r2->get() == 2021); + ASSERT(r2->set(2020) == 2020); + ASSERT(r2->get() == 2020); + cxx_test_suite_set_correct(); return nullptr; } From 968738f12741b34fd9b6b4e8888bd576ca46cd92 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 16 2020 23:25:04 +0000 Subject: [PATCH 280/2232] Optimize the computation of the methods of a struct --- diff --git a/Cargo.toml b/Cargo.toml index f9d9b52..eff5820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.9", path = "macro" } +itertools = "0.9" link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/gen/write.rs b/gen/write.rs index 7e1caac..21fbc8c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -3,6 +3,7 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; +use itertools::Itertools; use proc_macro2::Ident; pub(super) fn gen( @@ -44,6 +45,14 @@ pub(super) fn gen( } } + let methods_for_type = apis.iter().filter_map(|api| match api { + Api::RustFunction(efn) => match &efn.sig.receiver { + Some(rcvr) => Some((&rcvr.ident, efn)), + _ => None, + }, + _ => None, + }).into_group_map(); + for api in apis { match api { Api::Struct(strct) => { @@ -51,16 +60,12 @@ pub(super) fn gen( write_struct(out, strct); } Api::RustType(ety) => { - let methods = apis.iter().filter_map(|api| match api { - Api::RustFunction(efn) => match &efn.sig.receiver { - Some(rcvr) if rcvr.ident == ety.ident => Some(efn), - _ => None, + match methods_for_type.get(&ety.ident) { + Some(methods) => { + out.next_section(); + write_struct_with_methods(out, ety, methods); }, - _ => None, - }).collect::>(); - if !methods.is_empty() { - out.next_section(); - write_struct_with_methods(out, ety, methods); + _ => {} } } _ => {} @@ -316,12 +321,12 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: Vec<&ExternFn>) { +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &Vec<&ExternFn>) { for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } writeln!(out, "struct {} final {{", ety.ident); - for method in &methods { + for method in methods { write!(out, " "); let sig = &method.sig; let local_name = method.ident.to_string(); From f937996fc7c3b4ff9694c1afb36ea0901284e65c Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 16 2020 23:25:04 +0000 Subject: [PATCH 281/2232] Update documentation --- diff --git a/README.md b/README.md index f51a1ac..3f7399a 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,10 @@ mod ffi { // Functions implemented in C++. fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); + + // Methods implemented in C++. + fn get_name(self: &ThingC) -> &CxxString; } extern "Rust" { @@ -95,6 +97,9 @@ mod ffi { // Functions implemented in Rust. fn print_r(r: &ThingR); + + // Methods implemented in Rust. + fn print(self: &ThingR); } } ``` @@ -335,8 +340,6 @@ This is still early days for CXX; I am releasing it as a minimum viable product to collect feedback on the direction and invite collaborators. Here are some of the facets that I still intend for this project to tackle: -- [ ] Support associated methods: `extern "Rust" { fn f(self: &Struct); }` -- [ ] Support C++ member functions - [ ] Support structs with type parameters - [ ] Support async functions diff --git a/src/lib.rs b/src/lib.rs index 609db59..780a26e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,8 +79,10 @@ //! //! // Functions implemented in C++. //! fn make_demo(appname: &str) -> UniquePtr; -//! fn get_name(thing: &ThingC) -> &CxxString; //! fn do_thing(state: SharedThing); +//! +//! // Methods implemented in C++. +//! fn get_name(self: &ThingC) -> &CxxString; //! } //! //! extern "Rust" { @@ -90,6 +92,9 @@ //! //! // Functions implemented in Rust. //! fn print_r(r: &ThingR); +//! +//! // Methods implemented in Rust. +//! fn print(self: &ThingR); //! } //! } //! # @@ -99,6 +104,12 @@ //! # println!("called back with r={}", r.0); //! # } //! # +//! # impl ThingR { +//! # fn print(&self) { +//! # println!("method called back with r={}", self.0); +//! # } +//! # } +//! # //! # fn main() {} //! ``` //! From 187588eeacea37574d98e892d824776dce86867b Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 17 2020 23:19:54 +0000 Subject: [PATCH 282/2232] Mark default and copy constructors as deleted. --- diff --git a/gen/write.rs b/gen/write.rs index 21fbc8c..b8d17df 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -326,6 +326,8 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &Vec< writeln!(out, "//{}", line); } writeln!(out, "struct {} final {{", ety.ident); + writeln!(out, " {}() = delete;", ety.ident); + writeln!(out, " {}(const {}&) = delete;", ety.ident, ety.ident); for method in methods { write!(out, " "); let sig = &method.sig; From af74d661de1960843091514b2206d8bf1deeea82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:43:51 +0000 Subject: [PATCH 283/2232] Merge pull request #121 from jgalenson/methods Support calling C++ and Rust methods --- diff --git a/Cargo.toml b/Cargo.toml index f9d9b52..eff5820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.9", path = "macro" } +itertools = "0.9" link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/README.md b/README.md index f51a1ac..3f7399a 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,10 @@ mod ffi { // Functions implemented in C++. fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); + + // Methods implemented in C++. + fn get_name(self: &ThingC) -> &CxxString; } extern "Rust" { @@ -95,6 +97,9 @@ mod ffi { // Functions implemented in Rust. fn print_r(r: &ThingR); + + // Methods implemented in Rust. + fn print(self: &ThingR); } } ``` @@ -335,8 +340,6 @@ This is still early days for CXX; I am releasing it as a minimum viable product to collect feedback on the direction and invite collaborators. Here are some of the facets that I still intend for this project to tackle: -- [ ] Support associated methods: `extern "Rust" { fn f(self: &Struct); }` -- [ ] Support C++ member functions - [ ] Support structs with type parameters - [ ] Support async functions diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..bc0d976 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,13 +9,19 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } +const std::string &ThingC::get_name() const { + std::cout << "I'm a C++ method!" << std::endl; + return this->appname; +} + std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } -const std::string &get_name(const ThingC &thing) { return thing.appname; } - -void do_thing(SharedThing state) { print_r(*state.y); } +void do_thing(SharedThing state) { + print_r(*state.y); + state.y->print(); +} } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index fafc474..885293f 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -12,12 +12,13 @@ public: ~ThingC(); std::string appname; + + const std::string &get_name() const; }; struct SharedThing; std::unique_ptr make_demo(rust::Str appname); -const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); } // namespace example diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 66dfc79..713a1d1 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -11,13 +11,15 @@ mod ffi { type ThingC; fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; + fn get_name(self: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); + } extern "Rust" { type ThingR; fn print_r(r: &ThingR); + fn print(self: &ThingR); } } @@ -27,9 +29,15 @@ fn print_r(r: &ThingR) { println!("called back with r={}", r.0); } +impl ThingR { + fn print(&self) { + println!("method called back with r={}", self.0); + } +} + fn main() { let x = ffi::make_demo("demo of cxx::bridge"); - println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); + println!("this is a {}", x.as_ref().unwrap().get_name()); ffi::do_thing(ffi::SharedThing { z: 222, diff --git a/gen/write.rs b/gen/write.rs index 322ca08..b8d17df 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,8 @@ use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{Api, ExternFn, Signature, Struct, Type, Types, Var}; +use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; +use itertools::Itertools; use proc_macro2::Ident; pub(super) fn gen( @@ -44,10 +45,30 @@ pub(super) fn gen( } } + let methods_for_type = apis.iter().filter_map(|api| match api { + Api::RustFunction(efn) => match &efn.sig.receiver { + Some(rcvr) => Some((&rcvr.ident, efn)), + _ => None, + }, + _ => None, + }).into_group_map(); + for api in apis { - if let Api::Struct(strct) = api { - out.next_section(); - write_struct(out, strct); + match api { + Api::Struct(strct) => { + out.next_section(); + write_struct(out, strct); + } + Api::RustType(ety) => { + match methods_for_type.get(&ety.ident) { + Some(methods) => { + out.next_section(); + write_struct_with_methods(out, ety, methods); + }, + _ => {} + } + } + _ => {} } } @@ -300,6 +321,23 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &Vec<&ExternFn>) { + for line in ety.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", ety.ident); + writeln!(out, " {}() = delete;", ety.ident); + writeln!(out, " {}(const {}&) = delete;", ety.ident, ety.ident); + for method in methods { + write!(out, " "); + let sig = &method.sig; + let local_name = method.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, None, false); + writeln!(out, ";"); + } + writeln!(out, "}};"); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { @@ -326,9 +364,16 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + write!(out, "{}cxxbridge02${}${}(", out.namespace, receiver_type, efn.ident); + if let Some(base) = &efn.receiver { + write!(out, "{} *__receiver$", base.ident); + } for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { + if i > 0 || efn.receiver.is_some() { write!(out, ", "); } if arg.ty == RustString { @@ -347,14 +392,27 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, ") noexcept {{"); write!(out, " "); write_return_type(out, &efn.ret); - write!(out, "(*{}$)(", efn.ident); + match &efn.receiver { + None => write!(out, "(*{}$)(", efn.ident), + Some(base) => write!(out, "({}::*{}$)(", base.ident, efn.ident), + } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type(out, &arg.ty); } - writeln!(out, ") = {};", efn.ident); + write!(out, ")"); + match &efn.receiver { + Some(Receiver { mutability: None, ident: _ }) => write!(out, " const"), + _ => {}, + } + write!(out, " = "); + match &efn.receiver { + None => write!(out, "{}", efn.ident), + Some(base) => write!(out, "&{}::{}", base.ident, efn.ident), + } + writeln!(out, ";"); write!(out, " "); if efn.throws { writeln!(out, "::rust::Str::Repr throw$;"); @@ -377,7 +435,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } _ => {} } - write!(out, "{}$(", efn.ident); + match &efn.receiver { + None => write!(out, "{}$(", efn.ident), + Some(_) => write!(out, "(__receiver$->*{}$)(", efn.ident), + } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -452,7 +513,11 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let link_name = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -471,6 +536,10 @@ fn write_rust_function_decl_impl( } write!(out, "{}(", link_name); let mut needs_comma = false; + if let Some(base) = &sig.receiver { + write!(out, "{} &__receiver$", base.ident); + needs_comma = true; + } for arg in &sig.args { if needs_comma { write!(out, ", "); @@ -500,20 +569,26 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = efn.ident.to_string(); - let invoke = format!("{}cxxbridge02${}", out.namespace, efn.ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let invoke = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } -fn write_rust_function_shim_impl( +fn write_rust_function_shim_decl( out: &mut OutFile, local_name: &str, sig: &Signature, - types: &Types, - invoke: &str, + receiver: Option<&Receiver>, indirect_call: bool, ) { write_return_type(out, &sig.ret); + if let Some(base) = receiver { + write!(out, "{}::", base.ident); + } write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { @@ -532,6 +607,21 @@ fn write_rust_function_shim_impl( if !sig.throws { write!(out, " noexcept"); } +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &str, + indirect_call: bool, +) { + if out.header && sig.receiver.is_some() { + // We've already defined this inside the struct. + return; + } + write_rust_function_shim_decl(out, local_name, sig, sig.receiver.as_ref(), indirect_call); if out.header { writeln!(out, ";"); } else { @@ -570,8 +660,11 @@ fn write_rust_function_shim_impl( write!(out, "::rust::Str::Repr error$ = "); } write!(out, "{}(", invoke); + if let Some(_) = &sig.receiver { + write!(out, "*this"); + } for (i, arg) in sig.args.iter().enumerate() { - if i > 0 { + if i > 0 || sig.receiver.is_some() { write!(out, ", "); } match &arg.ty { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 146d21b..2d3166a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -123,6 +123,13 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; + let receiver = efn.receiver.iter().map(|base| { + let ident = &base.ident; + match base.mutability { + None => quote!(_: &#ident), + Some(_) => quote!(_: &mut #ident), + } + }); let args = efn.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -136,6 +143,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types quote!(#ident: #ty) } }); + let all_args = receiver.chain(args); let ret = if efn.throws { quote!(-> ::cxx::private::Result) } else { @@ -146,11 +154,15 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = format!("{}cxxbridge02${}", namespace, ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] - fn #local_name(#(#args,)* #outparam) #ret; + fn #local_name(#(#all_args,)* #outparam) #ret; } } @@ -158,7 +170,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); - let args = &efn.args; + let receiver = efn.receiver.iter().map(|base| match base.mutability { + None => quote!(&self), + Some(_) => quote!(&mut self), + }); + let args = efn.args.iter().map(|arg| quote!(#arg)); + let all_args = receiver.chain(args); let ret = if efn.throws { let ok = match &efn.ret { Some(ret) => quote!(#ret), @@ -169,7 +186,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types expand_return_type(&efn.ret) }; let indirect_return = indirect_return(efn, types); - let vars = efn.args.iter().map(|arg| { + let receiver_var = efn.receiver.iter().map(|_| quote!(self)); + let arg_vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { Type::Ident(ident) if ident == RustString => { @@ -189,6 +207,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types _ => quote!(#var), } }); + let vars = receiver_var.chain(arg_vars); let trampolines = efn .args .iter() @@ -274,18 +293,36 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } .unwrap_or(call); - quote! { - #doc - pub fn #ident(#args) #ret { - extern "C" { - #decl + let receiver_ident = efn.receiver.as_ref().map(|base| &base.ident); + match receiver_ident { + None => quote! { + #doc + pub fn #ident(#(#all_args,)*) #ret { + extern "C" { + #decl + } + #trampolines + unsafe { + #setup + #expr + } } - #trampolines - unsafe { - #setup - #expr + }, + Some(base_ident) => quote! { + #doc + impl #base_ident { + pub fn #ident(#(#all_args,)*) #ret { + extern "C" { + #decl + } + #trampolines + unsafe { + #setup + #expr + } + } } - } + }, } } @@ -333,7 +370,11 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let link_name = format!("{}cxxbridge02${}", namespace, ident); + let receiver_type = match &efn.receiver { + Some(base) => base.ident.to_string(), + None => "_".to_string(), + }; + let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); let invoke = Some(ident); @@ -355,6 +396,13 @@ fn expand_rust_function_shim_impl( catch_unwind_label: String, invoke: Option<&Ident>, ) -> TokenStream { + let receiver = sig.receiver.iter().map(|base| { + let ident = &base.ident; + match base.mutability { + None => quote!(__receiver: &#ident), + Some(_) => quote!(__receiver: &mut #ident), + } + }); let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -364,6 +412,7 @@ fn expand_rust_function_shim_impl( quote!(#ident: #ty) } }); + let all_args = receiver.chain(args); let vars = sig.args.iter().map(|arg| { let ident = &arg.ident; @@ -385,7 +434,10 @@ fn expand_rust_function_shim_impl( }); let mut call = match invoke { - Some(ident) => quote!(super::#ident), + Some(ident) => match sig.receiver { + None => quote!(super::#ident), + Some(_) => quote!(__receiver.#ident), + }, None => quote!(__extern), }; call.extend(quote! { (#(#vars),*) }); @@ -443,7 +495,7 @@ fn expand_rust_function_shim_impl( quote! { #[doc(hidden)] #[export_name = #link_name] - unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret { + unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret { let __fn = concat!(module_path!(), #catch_unwind_label); #expr } diff --git a/src/lib.rs b/src/lib.rs index 609db59..780a26e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,8 +79,10 @@ //! //! // Functions implemented in C++. //! fn make_demo(appname: &str) -> UniquePtr; -//! fn get_name(thing: &ThingC) -> &CxxString; //! fn do_thing(state: SharedThing); +//! +//! // Methods implemented in C++. +//! fn get_name(self: &ThingC) -> &CxxString; //! } //! //! extern "Rust" { @@ -90,6 +92,9 @@ //! //! // Functions implemented in Rust. //! fn print_r(r: &ThingR); +//! +//! // Methods implemented in Rust. +//! fn print(self: &ThingR); //! } //! } //! # @@ -99,6 +104,12 @@ //! # println!("called back with r={}", r.0); //! # } //! # +//! # impl ThingR { +//! # fn print(&self) { +//! # println!("method called back with r={}", self.0); +//! # } +//! # } +//! # //! # fn main() {} //! ``` //! diff --git a/syntax/check.rs b/syntax/check.rs index 181c530..3b6937a 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -195,6 +195,10 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } + if efn.receiver.is_some() { + reference_args += 1; + } + if reference_args != 1 { cx.error( efn, diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 8afa841..4da5740 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -49,10 +49,14 @@ pub mod ffi { fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; + + fn get(self: &C) -> usize; + fn set(self: &mut C, n: usize) -> usize; } extern "Rust" { type R; + type R2; fn r_return_primitive() -> usize; fn r_return_shared() -> Shared; @@ -77,11 +81,28 @@ pub mod ffi { fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; fn r_fail_return_primitive() -> Result; + + fn r_return_r2(n: usize) -> Box; + fn get(self: &R2) -> usize; + fn set(self: &mut R2, n: usize) -> usize; } } pub type R = usize; +pub struct R2(usize); + +impl R2 { + fn get(&self) -> usize { + self.0 + } + + fn set(&mut self, n: usize) -> usize { + self.0 = n; + n + } +} + #[derive(Debug)] struct Error; @@ -184,3 +205,7 @@ fn r_try_return_primitive() -> Result { fn r_fail_return_primitive() -> Result { Err(Error) } + +fn r_return_r2(n: usize) -> Box { + Box::new(R2(n)) +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d72bfd0..8893ee8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -15,6 +15,11 @@ C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } +size_t C::set(size_t n) { + this->n = n; + return this->n; +} + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -176,6 +181,13 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(std::strcmp(e.what(), "rust error") == 0); } + auto r2 = r_return_r2(2020); + ASSERT(r2->get() == 2020); + ASSERT(r2->set(2021) == 2021); + ASSERT(r2->get() == 2021); + ASSERT(r2->set(2020) == 2020); + ASSERT(r2->get() == 2020); + cxx_test_suite_set_correct(); return nullptr; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index a68be5c..43ac229 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -12,6 +12,7 @@ class C { public: C(size_t n); size_t get() const; + size_t set(size_t n); private: size_t n; diff --git a/tests/test.rs b/tests/test.rs index 511c52b..36de8f9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -107,6 +107,18 @@ fn test_c_call_r() { check!(cxx_run_test()); } +#[test] +fn test_c_method_calls() { + let mut unique_ptr = ffi::c_return_unique_ptr(); + + let old_value = unique_ptr.as_ref().unwrap().get(); + assert_eq!(2020, old_value); + assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021)); + assert_eq!(2021, unique_ptr.as_ref().unwrap().get()); + assert_eq!(old_value, unique_ptr.as_mut().unwrap().set(old_value)); + assert_eq!(old_value, unique_ptr.as_ref().unwrap().get()) +} + #[no_mangle] extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { Box::into_raw(Box::new(2020usize)) From 46a54e7abe3c38aba4d5f2afb69d7477c5d4f240 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 284/2232] Format with rustfmt 2020-03-11 --- diff --git a/gen/write.rs b/gen/write.rs index b8d17df..9a2e4c8 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -45,13 +45,16 @@ pub(super) fn gen( } } - let methods_for_type = apis.iter().filter_map(|api| match api { - Api::RustFunction(efn) => match &efn.sig.receiver { - Some(rcvr) => Some((&rcvr.ident, efn)), + let methods_for_type = apis + .iter() + .filter_map(|api| match api { + Api::RustFunction(efn) => match &efn.sig.receiver { + Some(rcvr) => Some((&rcvr.ident, efn)), + _ => None, + }, _ => None, - }, - _ => None, - }).into_group_map(); + }) + .into_group_map(); for api in apis { match api { @@ -59,15 +62,13 @@ pub(super) fn gen( out.next_section(); write_struct(out, strct); } - Api::RustType(ety) => { - match methods_for_type.get(&ety.ident) { - Some(methods) => { - out.next_section(); - write_struct_with_methods(out, ety, methods); - }, - _ => {} + Api::RustType(ety) => match methods_for_type.get(&ety.ident) { + Some(methods) => { + out.next_section(); + write_struct_with_methods(out, ety, methods); } - } + _ => {} + }, _ => {} } } @@ -368,7 +369,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { Some(base) => base.ident.to_string(), None => "_".to_string(), }; - write!(out, "{}cxxbridge02${}${}(", out.namespace, receiver_type, efn.ident); + write!( + out, + "{}cxxbridge02${}${}(", + out.namespace, receiver_type, efn.ident + ); if let Some(base) = &efn.receiver { write!(out, "{} *__receiver$", base.ident); } @@ -404,8 +409,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } write!(out, ")"); match &efn.receiver { - Some(Receiver { mutability: None, ident: _ }) => write!(out, " const"), - _ => {}, + Some(Receiver { + mutability: None, + ident: _, + }) => write!(out, " const"), + _ => {} } write!(out, " = "); match &efn.receiver { @@ -517,7 +525,10 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { Some(base) => base.ident.to_string(), None => "_".to_string(), }; - let link_name = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); + let link_name = format!( + "{}cxxbridge02${}${}", + out.namespace, receiver_type, efn.ident + ); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -573,7 +584,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { Some(base) => base.ident.to_string(), None => "_".to_string(), }; - let invoke = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident); + let invoke = format!( + "{}cxxbridge02${}${}", + out.namespace, receiver_type, efn.ident + ); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } From f94bef1ba2cf16b7b6ce1efe4bef88768b34d0a4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 285/2232] Replace itertools dependency --- diff --git a/Cargo.toml b/Cargo.toml index eff5820..f9d9b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.9", path = "macro" } -itertools = "0.9" link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/gen/write.rs b/gen/write.rs index 9a2e4c8..a77c658 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -3,8 +3,8 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; -use itertools::Itertools; use proc_macro2::Ident; +use std::collections::HashMap; pub(super) fn gen( namespace: Namespace, @@ -45,16 +45,17 @@ pub(super) fn gen( } } - let methods_for_type = apis - .iter() - .filter_map(|api| match api { - Api::RustFunction(efn) => match &efn.sig.receiver { - Some(rcvr) => Some((&rcvr.ident, efn)), - _ => None, - }, - _ => None, - }) - .into_group_map(); + let mut methods_for_type = HashMap::new(); + for api in apis { + if let Api::RustFunction(efn) = api { + if let Some(receiver) = &efn.sig.receiver { + methods_for_type + .entry(&receiver.ident) + .or_insert_with(Vec::new) + .push(efn); + } + } + } for api in apis { match api { From 5e29b217c3226866587bff4a572d9c1e55f99aff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 286/2232] Rely on UniquePtr Deref impl in tests --- diff --git a/tests/test.rs b/tests/test.rs index 36de8f9..eb14d36 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -31,14 +31,7 @@ fn test_c_return() { assert_eq!("2020", ffi::c_return_str(&shared)); assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); - assert_eq!( - "2020", - ffi::c_return_unique_ptr_string() - .as_ref() - .unwrap() - .to_str() - .unwrap() - ); + assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); } #[test] @@ -54,13 +47,7 @@ fn test_c_try_return() { assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap()); assert_eq!("2020", ffi::c_try_return_rust_string().unwrap()); - assert_eq!( - "2020", - ffi::c_try_return_unique_ptr_string() - .unwrap() - .as_ref() - .unwrap() - ); + assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap()); } #[test] @@ -70,7 +57,7 @@ fn test_c_take() { check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); - check!(ffi::c_take_ref_c(unique_ptr.as_ref().unwrap())); + check!(ffi::c_take_ref_c(&unique_ptr)); check!(ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); @@ -111,12 +98,12 @@ fn test_c_call_r() { fn test_c_method_calls() { let mut unique_ptr = ffi::c_return_unique_ptr(); - let old_value = unique_ptr.as_ref().unwrap().get(); + let old_value = unique_ptr.get(); assert_eq!(2020, old_value); - assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021)); - assert_eq!(2021, unique_ptr.as_ref().unwrap().get()); - assert_eq!(old_value, unique_ptr.as_mut().unwrap().set(old_value)); - assert_eq!(old_value, unique_ptr.as_ref().unwrap().get()) + assert_eq!(2021, unique_ptr.set(2021)); + assert_eq!(2021, unique_ptr.get()); + assert_eq!(old_value, unique_ptr.set(old_value)); + assert_eq!(old_value, unique_ptr.get()) } #[no_mangle] From b6a5f67b193f861dc89cb874bbdcb8d96118a722 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 287/2232] Remove methods from intro example code I'd like to keep this first code snippet as focused as possible on the most important concepts. We'll need to figure out somewhere else to put exhaustive documentation of the full feature set. --- diff --git a/README.md b/README.md index 3f7399a..508d43d 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,8 @@ mod ffi { // Functions implemented in C++. fn make_demo(appname: &str) -> UniquePtr; + fn get_name(thing: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); - - // Methods implemented in C++. - fn get_name(self: &ThingC) -> &CxxString; } extern "Rust" { @@ -97,9 +95,6 @@ mod ffi { // Functions implemented in Rust. fn print_r(r: &ThingR); - - // Methods implemented in Rust. - fn print(self: &ThingR); } } ``` diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index bc0d976..cd447ea 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,19 +9,13 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -const std::string &ThingC::get_name() const { - std::cout << "I'm a C++ method!" << std::endl; - return this->appname; -} - std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } -void do_thing(SharedThing state) { - print_r(*state.y); - state.y->print(); -} +const std::string &get_name(const ThingC &thing) { return thing.appname; } + +void do_thing(SharedThing state) { print_r(*state.y); } } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 885293f..fafc474 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -12,13 +12,12 @@ public: ~ThingC(); std::string appname; - - const std::string &get_name() const; }; struct SharedThing; std::unique_ptr make_demo(rust::Str appname); +const std::string &get_name(const ThingC &thing); void do_thing(SharedThing state); } // namespace example diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 713a1d1..66dfc79 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -11,15 +11,13 @@ mod ffi { type ThingC; fn make_demo(appname: &str) -> UniquePtr; - fn get_name(self: &ThingC) -> &CxxString; + fn get_name(thing: &ThingC) -> &CxxString; fn do_thing(state: SharedThing); - } extern "Rust" { type ThingR; fn print_r(r: &ThingR); - fn print(self: &ThingR); } } @@ -29,15 +27,9 @@ fn print_r(r: &ThingR) { println!("called back with r={}", r.0); } -impl ThingR { - fn print(&self) { - println!("method called back with r={}", self.0); - } -} - fn main() { let x = ffi::make_demo("demo of cxx::bridge"); - println!("this is a {}", x.as_ref().unwrap().get_name()); + println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); ffi::do_thing(ffi::SharedThing { z: 222, diff --git a/src/lib.rs b/src/lib.rs index 780a26e..609db59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,10 +79,8 @@ //! //! // Functions implemented in C++. //! fn make_demo(appname: &str) -> UniquePtr; +//! fn get_name(thing: &ThingC) -> &CxxString; //! fn do_thing(state: SharedThing); -//! -//! // Methods implemented in C++. -//! fn get_name(self: &ThingC) -> &CxxString; //! } //! //! extern "Rust" { @@ -92,9 +90,6 @@ //! //! // Functions implemented in Rust. //! fn print_r(r: &ThingR); -//! -//! // Methods implemented in Rust. -//! fn print(self: &ThingR); //! } //! } //! # @@ -104,12 +99,6 @@ //! # println!("called back with r={}", r.0); //! # } //! # -//! # impl ThingR { -//! # fn print(&self) { -//! # println!("method called back with r={}", self.0); -//! # } -//! # } -//! # //! # fn main() {} //! ``` //! From 70711f6d987c7fc929ba326280cde26cc24efa8a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 288/2232] Run exhaustive test suite on 1.43+ only The `self: &T` syntax in extern functions doesn't work yet in 1.42. The intro demo code should work though. --- diff --git a/.travis.yml b/.travis.yml index 28ad110..49bacbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: rust rust: - nightly - beta - - stable script: - cargo run --manifest-path demo-rs/Cargo.toml @@ -55,3 +54,5 @@ matrix: - bazel test ... --verbose_failures --noshow_progress - name: Minimum rustc rust: 1.42.0 + script: + - cargo run --manifest-path demo-rs/Cargo.toml From c1fe0055ca8fb90c27faecc5772eb9f7459b8722 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:44:03 +0000 Subject: [PATCH 289/2232] Touch up PR 121 --- diff --git a/gen/write.rs b/gen/write.rs index a77c658..4c7b0ae 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -63,13 +63,12 @@ pub(super) fn gen( out.next_section(); write_struct(out, strct); } - Api::RustType(ety) => match methods_for_type.get(&ety.ident) { - Some(methods) => { + Api::RustType(ety) => { + if let Some(methods) = methods_for_type.get(&ety.ident) { out.next_section(); write_struct_with_methods(out, ety, methods); } - _ => {} - }, + } _ => {} } } @@ -323,7 +322,7 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &Vec<&ExternFn>) { +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } From 8537f2ac8f3c0d51b96e944dfd5f3da87ef9e54d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 17 2020 23:58:18 +0000 Subject: [PATCH 290/2232] Update bazel CI to 1.43-beta to support extern method syntax --- diff --git a/WORKSPACE b/WORKSPACE index 4357b70..435086f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,15 +24,17 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_42_linux", + name = "rust_1_43_beta_linux", exec_triple = "x86_64-unknown-linux-gnu", extra_target_triples = [], - version = "1.42.0", + iso_date = "2020-04-07", + version = "beta", ) rust_repository_set( - name = "rust_1_42_darwin", + name = "rust_1_43_beta_darwin", exec_triple = "x86_64-apple-darwin", extra_target_triples = [], - version = "1.42.0", + iso_date = "2020-04-07", + version = "beta", ) From de96ae805a0409f2b5732278bad4b6f0923c5a08 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 19 2020 17:52:35 +0000 Subject: [PATCH 291/2232] Pull in 1.43.0-beta.6 --- diff --git a/WORKSPACE b/WORKSPACE index 435086f..f39808c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -27,7 +27,7 @@ rust_repository_set( name = "rust_1_43_beta_linux", exec_triple = "x86_64-unknown-linux-gnu", extra_target_triples = [], - iso_date = "2020-04-07", + iso_date = "2020-04-19", version = "beta", ) @@ -35,6 +35,6 @@ rust_repository_set( name = "rust_1_43_beta_darwin", exec_triple = "x86_64-apple-darwin", extra_target_triples = [], - iso_date = "2020-04-07", + iso_date = "2020-04-19", version = "beta", ) From 44395e335b603764a3fe2b3bc6b0055168999a38 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 19 2020 21:52:55 +0000 Subject: [PATCH 292/2232] Match clang-format's style more consistently in generated code --- diff --git a/gen/write.rs b/gen/write.rs index 4c7b0ae..460a55f 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -328,7 +328,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } writeln!(out, "struct {} final {{", ety.ident); writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {}&) = delete;", ety.ident, ety.ident); + writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); for method in methods { write!(out, " "); let sig = &method.sig; From 26804bd83b5e37da5338971e626c835fe2b52fdc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 03:06:51 +0000 Subject: [PATCH 293/2232] Use receiver name that resembles the Rust input more closely --- diff --git a/gen/write.rs b/gen/write.rs index 460a55f..7a57111 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -375,7 +375,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { out.namespace, receiver_type, efn.ident ); if let Some(base) = &efn.receiver { - write!(out, "{} *__receiver$", base.ident); + write!(out, "{} *self$", base.ident); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -445,7 +445,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } match &efn.receiver { None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(__receiver$->*{}$)(", efn.ident), + Some(_) => write!(out, "(self$->*{}$)(", efn.ident), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -548,7 +548,7 @@ fn write_rust_function_decl_impl( write!(out, "{}(", link_name); let mut needs_comma = false; if let Some(base) = &sig.receiver { - write!(out, "{} &__receiver$", base.ident); + write!(out, "{} &self$", base.ident); needs_comma = true; } for arg in &sig.args { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2d3166a..ac39e4e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -399,8 +399,8 @@ fn expand_rust_function_shim_impl( let receiver = sig.receiver.iter().map(|base| { let ident = &base.ident; match base.mutability { - None => quote!(__receiver: &#ident), - Some(_) => quote!(__receiver: &mut #ident), + None => quote!(__self: &#ident), + Some(_) => quote!(__self: &mut #ident), } }); let args = sig.args.iter().map(|arg| { @@ -436,7 +436,7 @@ fn expand_rust_function_shim_impl( let mut call = match invoke { Some(ident) => match sig.receiver { None => quote!(super::#ident), - Some(_) => quote!(__receiver.#ident), + Some(_) => quote!(__self.#ident), }, None => quote!(__extern), }; From 0841930d34460e531d6397c34513b1fbb43c8383 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 03:38:28 +0000 Subject: [PATCH 294/2232] Share one Namespace type between gen and macro --- diff --git a/gen/mod.rs b/gen/mod.rs index 523ba1d..998cd14 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -3,12 +3,11 @@ mod error; pub(super) mod include; -mod namespace; pub(super) mod out; mod write; use self::error::format_err; -use self::namespace::Namespace; +use crate::syntax::namespace::Namespace; use crate::syntax::{self, check, ident, Types}; use quote::quote; use std::fs; diff --git a/gen/namespace.rs b/gen/namespace.rs deleted file mode 100644 index 557e331..0000000 --- a/gen/namespace.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::fmt::{self, Display}; -use std::slice::Iter; - -#[derive(Clone)] -pub struct Namespace { - segments: Vec, -} - -impl Namespace { - pub fn new(segments: Vec) -> Self { - Namespace { segments } - } - - pub fn iter(&self) -> Iter { - self.segments.iter() - } -} - -impl Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for segment in self { - f.write_str(segment)?; - f.write_str("$")?; - } - Ok(()) - } -} - -impl<'a> IntoIterator for &'a Namespace { - type Item = &'a String; - type IntoIter = Iter<'a, String>; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} diff --git a/gen/out.rs b/gen/out.rs index 35f2cd7..08bf85f 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -1,5 +1,5 @@ use crate::gen::include::Includes; -use crate::gen::namespace::Namespace; +use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; diff --git a/gen/write.rs b/gen/write.rs index 7a57111..28a2d3b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,7 +1,7 @@ -use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ac39e4e..373bfee 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,5 @@ -use crate::namespace::Namespace; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 59433c1..49c4f42 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,7 +13,7 @@ mod expand; mod namespace; mod syntax; -use crate::namespace::Namespace; +use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemMod}; diff --git a/macro/src/namespace.rs b/macro/src/namespace.rs index d2b3e1c..2d14aa2 100644 --- a/macro/src/namespace.rs +++ b/macro/src/namespace.rs @@ -1,4 +1,5 @@ use crate::syntax::ident; +use crate::syntax::namespace::Namespace; use quote::IdentFragment; use std::fmt::{self, Display}; use syn::parse::{Parse, ParseStream, Result}; @@ -8,10 +9,6 @@ mod kw { syn::custom_keyword!(namespace); } -pub struct Namespace { - segments: Vec, -} - impl Parse for Namespace { fn parse(input: ParseStream) -> Result { let mut segments = Vec::new(); @@ -25,17 +22,7 @@ impl Parse for Namespace { } input.parse::>()?; } - Ok(Namespace { segments }) - } -} - -impl Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for segment in &self.segments { - f.write_str(segment)?; - f.write_str("$")?; - } - Ok(()) + Ok(Namespace::new(segments)) } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 4eaf81e..21ff447 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod doc; pub mod error; pub mod ident; mod impls; +pub mod namespace; mod parse; pub mod set; mod tokens; diff --git a/syntax/namespace.rs b/syntax/namespace.rs new file mode 100644 index 0000000..557e331 --- /dev/null +++ b/syntax/namespace.rs @@ -0,0 +1,35 @@ +use std::fmt::{self, Display}; +use std::slice::Iter; + +#[derive(Clone)] +pub struct Namespace { + segments: Vec, +} + +impl Namespace { + pub fn new(segments: Vec) -> Self { + Namespace { segments } + } + + pub fn iter(&self) -> Iter { + self.segments.iter() + } +} + +impl Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for segment in self { + f.write_str(segment)?; + f.write_str("$")?; + } + Ok(()) + } +} + +impl<'a> IntoIterator for &'a Namespace { + type Item = &'a String; + type IntoIter = Iter<'a, String>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} From b6cf314a45b2cdda3e6e996fe034bbc83d8619b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 03:59:20 +0000 Subject: [PATCH 295/2232] Unify Namespace parsing code --- diff --git a/gen/mod.rs b/gen/mod.rs index 998cd14..4c3a292 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -8,13 +8,12 @@ mod write; use self::error::format_err; use crate::syntax::namespace::Namespace; -use crate::syntax::{self, check, ident, Types}; +use crate::syntax::{self, check, Types}; use quote::quote; use std::fs; use std::io; use std::path::Path; -use syn::parse::ParseStream; -use syn::{Attribute, File, Item, Token}; +use syn::{Attribute, File, Item}; use thiserror::Error; pub(super) type Result = std::result::Result; @@ -86,8 +85,7 @@ fn find_bridge_mod(syntax: File) -> Result { ))); } }; - let namespace_segments = parse_args(attr)?; - let namespace = Namespace::new(namespace_segments); + let namespace = parse_args(attr)?; return Ok(Input { namespace, module }); } } @@ -96,24 +94,10 @@ fn find_bridge_mod(syntax: File) -> Result { Err(Error::NoBridgeMod) } -fn parse_args(attr: &Attribute) -> syn::Result> { +fn parse_args(attr: &Attribute) -> syn::Result { if attr.tokens.is_empty() { - return Ok(Vec::new()); + Ok(Namespace::none()) + } else { + attr.parse_args() } - attr.parse_args_with(|input: ParseStream| { - mod kw { - syn::custom_keyword!(namespace); - } - input.parse::()?; - input.parse::()?; - let path = syn::Path::parse_mod_style(input)?; - input.parse::>()?; - path.segments - .into_iter() - .map(|seg| { - ident::check(&seg.ident)?; - Ok(seg.ident.to_string()) - }) - .collect() - }) } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 49c4f42..b56f58e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -10,7 +10,6 @@ extern crate proc_macro; mod expand; -mod namespace; mod syntax; use crate::syntax::namespace::Namespace; diff --git a/macro/src/namespace.rs b/macro/src/namespace.rs deleted file mode 100644 index 2d14aa2..0000000 --- a/macro/src/namespace.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::syntax::ident; -use crate::syntax::namespace::Namespace; -use quote::IdentFragment; -use std::fmt::{self, Display}; -use syn::parse::{Parse, ParseStream, Result}; -use syn::{Path, Token}; - -mod kw { - syn::custom_keyword!(namespace); -} - -impl Parse for Namespace { - fn parse(input: ParseStream) -> Result { - let mut segments = Vec::new(); - if !input.is_empty() { - input.parse::()?; - input.parse::()?; - let path = input.call(Path::parse_mod_style)?; - for segment in path.segments { - ident::check(&segment.ident)?; - segments.push(segment.ident.to_string()); - } - input.parse::>()?; - } - Ok(Namespace::new(segments)) - } -} - -impl IdentFragment for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 557e331..d26bb9e 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,5 +1,13 @@ +use crate::syntax::ident; +use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; +use syn::parse::{Parse, ParseStream, Result}; +use syn::{Path, Token}; + +mod kw { + syn::custom_keyword!(namespace); +} #[derive(Clone)] pub struct Namespace { @@ -7,8 +15,10 @@ pub struct Namespace { } impl Namespace { - pub fn new(segments: Vec) -> Self { - Namespace { segments } + pub fn none() -> Self { + Namespace { + segments: Vec::new(), + } } pub fn iter(&self) -> Iter { @@ -16,6 +26,23 @@ impl Namespace { } } +impl Parse for Namespace { + fn parse(input: ParseStream) -> Result { + let mut segments = Vec::new(); + if !input.is_empty() { + input.parse::()?; + input.parse::()?; + let path = input.call(Path::parse_mod_style)?; + for segment in path.segments { + ident::check(&segment.ident)?; + segments.push(segment.ident.to_string()); + } + input.parse::>()?; + } + Ok(Namespace { segments }) + } +} + impl Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for segment in self { @@ -26,6 +53,12 @@ impl Display for Namespace { } } +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 String; type IntoIter = Iter<'a, String>; From f878e591a650439a168de5a112cd953be6c0bdb3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 04:12:56 +0000 Subject: [PATCH 296/2232] Merge pull request #127 from dtolnay/namespace Unify the Namespace types used by both code generators --- diff --git a/gen/mod.rs b/gen/mod.rs index 523ba1d..4c3a292 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -3,19 +3,17 @@ mod error; pub(super) mod include; -mod namespace; pub(super) mod out; mod write; use self::error::format_err; -use self::namespace::Namespace; -use crate::syntax::{self, check, ident, Types}; +use crate::syntax::namespace::Namespace; +use crate::syntax::{self, check, Types}; use quote::quote; use std::fs; use std::io; use std::path::Path; -use syn::parse::ParseStream; -use syn::{Attribute, File, Item, Token}; +use syn::{Attribute, File, Item}; use thiserror::Error; pub(super) type Result = std::result::Result; @@ -87,8 +85,7 @@ fn find_bridge_mod(syntax: File) -> Result { ))); } }; - let namespace_segments = parse_args(attr)?; - let namespace = Namespace::new(namespace_segments); + let namespace = parse_args(attr)?; return Ok(Input { namespace, module }); } } @@ -97,24 +94,10 @@ fn find_bridge_mod(syntax: File) -> Result { Err(Error::NoBridgeMod) } -fn parse_args(attr: &Attribute) -> syn::Result> { +fn parse_args(attr: &Attribute) -> syn::Result { if attr.tokens.is_empty() { - return Ok(Vec::new()); + Ok(Namespace::none()) + } else { + attr.parse_args() } - attr.parse_args_with(|input: ParseStream| { - mod kw { - syn::custom_keyword!(namespace); - } - input.parse::()?; - input.parse::()?; - let path = syn::Path::parse_mod_style(input)?; - input.parse::>()?; - path.segments - .into_iter() - .map(|seg| { - ident::check(&seg.ident)?; - Ok(seg.ident.to_string()) - }) - .collect() - }) } diff --git a/gen/namespace.rs b/gen/namespace.rs deleted file mode 100644 index 557e331..0000000 --- a/gen/namespace.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::fmt::{self, Display}; -use std::slice::Iter; - -#[derive(Clone)] -pub struct Namespace { - segments: Vec, -} - -impl Namespace { - pub fn new(segments: Vec) -> Self { - Namespace { segments } - } - - pub fn iter(&self) -> Iter { - self.segments.iter() - } -} - -impl Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for segment in self { - f.write_str(segment)?; - f.write_str("$")?; - } - Ok(()) - } -} - -impl<'a> IntoIterator for &'a Namespace { - type Item = &'a String; - type IntoIter = Iter<'a, String>; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} diff --git a/gen/out.rs b/gen/out.rs index 35f2cd7..08bf85f 100644 --- a/gen/out.rs +++ b/gen/out.rs @@ -1,5 +1,5 @@ use crate::gen::include::Includes; -use crate::gen::namespace::Namespace; +use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; diff --git a/gen/write.rs b/gen/write.rs index 7a57111..28a2d3b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,7 +1,7 @@ -use crate::gen::namespace::Namespace; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ac39e4e..373bfee 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,5 @@ -use crate::namespace::Namespace; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 59433c1..b56f58e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -10,10 +10,9 @@ extern crate proc_macro; mod expand; -mod namespace; mod syntax; -use crate::namespace::Namespace; +use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemMod}; diff --git a/macro/src/namespace.rs b/macro/src/namespace.rs deleted file mode 100644 index d2b3e1c..0000000 --- a/macro/src/namespace.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::syntax::ident; -use quote::IdentFragment; -use std::fmt::{self, Display}; -use syn::parse::{Parse, ParseStream, Result}; -use syn::{Path, Token}; - -mod kw { - syn::custom_keyword!(namespace); -} - -pub struct Namespace { - segments: Vec, -} - -impl Parse for Namespace { - fn parse(input: ParseStream) -> Result { - let mut segments = Vec::new(); - if !input.is_empty() { - input.parse::()?; - input.parse::()?; - let path = input.call(Path::parse_mod_style)?; - for segment in path.segments { - ident::check(&segment.ident)?; - segments.push(segment.ident.to_string()); - } - input.parse::>()?; - } - Ok(Namespace { segments }) - } -} - -impl Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for segment in &self.segments { - f.write_str(segment)?; - f.write_str("$")?; - } - Ok(()) - } -} - -impl IdentFragment for Namespace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} diff --git a/syntax/mod.rs b/syntax/mod.rs index 4eaf81e..21ff447 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod doc; pub mod error; pub mod ident; mod impls; +pub mod namespace; mod parse; pub mod set; mod tokens; diff --git a/syntax/namespace.rs b/syntax/namespace.rs new file mode 100644 index 0000000..d26bb9e --- /dev/null +++ b/syntax/namespace.rs @@ -0,0 +1,68 @@ +use crate::syntax::ident; +use quote::IdentFragment; +use std::fmt::{self, Display}; +use std::slice::Iter; +use syn::parse::{Parse, ParseStream, Result}; +use syn::{Path, Token}; + +mod kw { + syn::custom_keyword!(namespace); +} + +#[derive(Clone)] +pub struct Namespace { + segments: Vec, +} + +impl Namespace { + pub fn none() -> Self { + Namespace { + segments: Vec::new(), + } + } + + pub fn iter(&self) -> Iter { + self.segments.iter() + } +} + +impl Parse for Namespace { + fn parse(input: ParseStream) -> Result { + let mut segments = Vec::new(); + if !input.is_empty() { + input.parse::()?; + input.parse::()?; + let path = input.call(Path::parse_mod_style)?; + for segment in path.segments { + ident::check(&segment.ident)?; + segments.push(segment.ident.to_string()); + } + input.parse::>()?; + } + Ok(Namespace { segments }) + } +} + +impl Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for segment in self { + f.write_str(segment)?; + f.write_str("$")?; + } + 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 String; + type IntoIter = Iter<'a, String>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} From 4e7123f26f72508d03aeb27376e6cad309128611 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 04:13:05 +0000 Subject: [PATCH 297/2232] Resolve clippy single_match lint --- diff --git a/gen/write.rs b/gen/write.rs index 28a2d3b..9ae114d 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -408,12 +408,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_type(out, &arg.ty); } write!(out, ")"); - match &efn.receiver { - Some(Receiver { - mutability: None, - ident: _, - }) => write!(out, " const"), - _ => {} + if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } } write!(out, " = "); match &efn.receiver { From 9b5cfe183644ef66d068c40a8a8aa41781a205f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 04:13:06 +0000 Subject: [PATCH 298/2232] Resolve clippy redundant_pattern_matching lint --- diff --git a/gen/write.rs b/gen/write.rs index 9ae114d..59e12ed 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -672,7 +672,7 @@ fn write_rust_function_shim_impl( write!(out, "::rust::Str::Repr error$ = "); } write!(out, "{}(", invoke); - if let Some(_) = &sig.receiver { + if sig.receiver.is_some() { write!(out, "*this"); } for (i, arg) in sig.args.iter().enumerate() { From 3caa50ac99cb77fca136906299b5d1486cf26759 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 04:38:44 +0000 Subject: [PATCH 299/2232] Share function link name mangling logic --- diff --git a/gen/write.rs b/gen/write.rs index 59e12ed..4df404c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,9 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; -use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; @@ -365,15 +367,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - write!( - out, - "{}cxxbridge02${}${}(", - out.namespace, receiver_type, efn.ident - ); + let mangled = mangle::extern_fn(&out.namespace, efn); + write!(out, "{}(", mangled); if let Some(base) = &efn.receiver { write!(out, "{} *self$", base.ident); } @@ -519,14 +514,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!( - "{}cxxbridge02${}${}", - out.namespace, receiver_type, efn.ident - ); + let link_name = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -578,14 +566,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = efn.ident.to_string(); - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let invoke = format!( - "{}cxxbridge02${}${}", - out.namespace, receiver_type, efn.ident - ); + let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 373bfee..d8bd241 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,6 +1,8 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; -use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; +use crate::syntax::{ + self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, +}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{parse_quote, Error, ItemMod, Result, Token}; @@ -154,11 +156,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); + let link_name = mangle::extern_fn(namespace, efn); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] @@ -370,11 +368,7 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); + let link_name = mangle::extern_fn(namespace, efn); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); let invoke = Some(ident); diff --git a/syntax/mangle.rs b/syntax/mangle.rs new file mode 100644 index 0000000..9e6bb8a --- /dev/null +++ b/syntax/mangle.rs @@ -0,0 +1,10 @@ +use crate::syntax::namespace::Namespace; +use crate::syntax::ExternFn; + +pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { + let receiver_type = match &efn.receiver { + Some(receiver) => receiver.ident.to_string(), + None => "_".to_string(), + }; + format!("{}cxxbridge02${}${}", namespace, receiver_type, efn.ident) +} diff --git a/syntax/mod.rs b/syntax/mod.rs index 21ff447..b7ca6b7 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod doc; pub mod error; pub mod ident; mod impls; +pub mod mangle; pub mod namespace; mod parse; pub mod set; From 4d46c03231e833dcf608551b96a4b3e07df6778d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 06:34:50 +0000 Subject: [PATCH 300/2232] Merge pull request #128 from dtolnay/mangle Share function link name mangling logic --- diff --git a/gen/write.rs b/gen/write.rs index 59e12ed..4df404c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,7 +2,9 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; -use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; @@ -365,15 +367,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } else { write_extern_return_type_space(out, &efn.ret, types); } - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - write!( - out, - "{}cxxbridge02${}${}(", - out.namespace, receiver_type, efn.ident - ); + let mangled = mangle::extern_fn(&out.namespace, efn); + write!(out, "{}(", mangled); if let Some(base) = &efn.receiver { write!(out, "{} *self$", base.ident); } @@ -519,14 +514,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!( - "{}cxxbridge02${}${}", - out.namespace, receiver_type, efn.ident - ); + let link_name = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -578,14 +566,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = efn.ident.to_string(); - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let invoke = format!( - "{}cxxbridge02${}${}", - out.namespace, receiver_type, efn.ident - ); + let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 373bfee..d8bd241 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,6 +1,8 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; -use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types}; +use crate::syntax::{ + self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, +}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{parse_quote, Error, ItemMod, Result, Token}; @@ -154,11 +156,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); + let link_name = mangle::extern_fn(namespace, efn); let local_name = format_ident!("__{}", ident); quote! { #[link_name = #link_name] @@ -370,11 +368,7 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let receiver_type = match &efn.receiver { - Some(base) => base.ident.to_string(), - None => "_".to_string(), - }; - let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident); + let link_name = mangle::extern_fn(namespace, efn); let local_name = format_ident!("__{}", ident); let catch_unwind_label = format!("::{}", ident); let invoke = Some(ident); diff --git a/syntax/mangle.rs b/syntax/mangle.rs new file mode 100644 index 0000000..9e6bb8a --- /dev/null +++ b/syntax/mangle.rs @@ -0,0 +1,10 @@ +use crate::syntax::namespace::Namespace; +use crate::syntax::ExternFn; + +pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { + let receiver_type = match &efn.receiver { + Some(receiver) => receiver.ident.to_string(), + None => "_".to_string(), + }; + format!("{}cxxbridge02${}${}", namespace, receiver_type, efn.ident) +} diff --git a/syntax/mod.rs b/syntax/mod.rs index 21ff447..b7ca6b7 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod doc; pub mod error; pub mod ident; mod impls; +pub mod mangle; pub mod namespace; mod parse; pub mod set; From 9d8d80bd811811067c6bae4f13cbdea152550887 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 06:35:02 +0000 Subject: [PATCH 301/2232] Remove '_' segment from symbol of non-associated functions --- diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 9e6bb8a..a109951 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -2,9 +2,9 @@ use crate::syntax::namespace::Namespace; use crate::syntax::ExternFn; pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { - let receiver_type = match &efn.receiver { - Some(receiver) => receiver.ident.to_string(), - None => "_".to_string(), + let receiver = match &efn.receiver { + Some(receiver) => receiver.ident.to_string() + "$", + None => String::new(), }; - format!("{}cxxbridge02${}${}", namespace, receiver_type, efn.ident) + format!("{}cxxbridge02${}{}", namespace, receiver, efn.ident) } From 5ea922a2112e6208df99354aad02a61beb3468ab Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 06:35:54 +0000 Subject: [PATCH 302/2232] Centralize mangled symbol joining --- diff --git a/syntax/mangle.rs b/syntax/mangle.rs index a109951..f2616d2 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,10 +1,18 @@ use crate::syntax::namespace::Namespace; -use crate::syntax::ExternFn; +use crate::syntax::{symbol, ExternFn}; -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { - let receiver = match &efn.receiver { - Some(receiver) => receiver.ident.to_string() + "$", - None => String::new(), +const CXXBRIDGE: &str = "cxxbridge02"; + +macro_rules! join { + ($($segment:expr),*) => { + symbol::join(&[$(&$segment),*]) }; - format!("{}cxxbridge02${}{}", namespace, receiver, efn.ident) +} + +pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { + match &efn.receiver { + Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ident, efn.ident), + None => join!(namespace, CXXBRIDGE, efn.ident), + } + .to_string() } diff --git a/syntax/mod.rs b/syntax/mod.rs index b7ca6b7..81b1a2f 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -11,6 +11,7 @@ pub mod mangle; pub mod namespace; mod parse; pub mod set; +mod symbol; mod tokens; pub mod types; diff --git a/syntax/symbol.rs b/syntax/symbol.rs new file mode 100644 index 0000000..a40baaf --- /dev/null +++ b/syntax/symbol.rs @@ -0,0 +1,66 @@ +use crate::syntax::namespace::Namespace; +use proc_macro2::{Ident, TokenStream}; +use quote::ToTokens; +use std::fmt::{self, Display, Write}; + +// A mangled symbol consisting of segments separated by '$'. +// For example: cxxbridge02$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 trait Segment: Display { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} + +impl Segment for str {} +impl Segment for Ident {} + +impl Segment for Namespace { + fn write(&self, symbol: &mut Symbol) { + for segment in self { + symbol.push(segment); + } + } +} + +impl Segment for &'_ T +where + T: ?Sized + Segment, +{ + 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 +} From 891061bc7fdaceeebe81afb0ec195a63868f77f6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 06:35:54 +0000 Subject: [PATCH 303/2232] Use Symbol for mangled names throughout code generators --- diff --git a/gen/write.rs b/gen/write.rs index 4df404c..c100691 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -2,6 +2,7 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::symbol::Symbol; use crate::syntax::{ mangle, Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var, }; @@ -334,7 +335,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.to_string(); + let local_name = Symbol::from(&method.ident); write_rust_function_shim_decl(out, &local_name, sig, None, false); writeln!(out, ";"); } @@ -504,12 +505,12 @@ fn write_function_pointer_trampoline( types: &Types, ) { out.next_section(); - let r_trampoline = format!("{}cxxbridge02${}${}$1", out.namespace, efn.ident, var); + let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = format!("{}cxxbridge02${}${}$0", out.namespace, efn.ident, var); + let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } @@ -521,7 +522,7 @@ fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { fn write_rust_function_decl_impl( out: &mut OutFile, - link_name: &str, + link_name: &Symbol, sig: &Signature, types: &Types, indirect_call: bool, @@ -565,7 +566,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } - let local_name = efn.ident.to_string(); + let local_name = Symbol::from(&efn.ident); let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); @@ -573,7 +574,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { fn write_rust_function_shim_decl( out: &mut OutFile, - local_name: &str, + local_name: &Symbol, sig: &Signature, receiver: Option<&Receiver>, indirect_call: bool, @@ -604,10 +605,10 @@ fn write_rust_function_shim_decl( fn write_rust_function_shim_impl( out: &mut OutFile, - local_name: &str, + local_name: &Symbol, sig: &Signature, types: &Types, - invoke: &str, + invoke: &Symbol, indirect_call: bool, ) { if out.header && sig.receiver.is_some() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d8bd241..2155ba0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; @@ -331,8 +332,8 @@ fn expand_function_pointer_trampoline( sig: &Signature, types: &Types, ) -> TokenStream { - let c_trampoline = format!("{}cxxbridge02${}${}$0", namespace, efn.ident, var); - let r_trampoline = format!("{}cxxbridge02${}${}$1", namespace, efn.ident, var); + let c_trampoline = mangle::c_trampoline(namespace, efn, var); + let r_trampoline = mangle::r_trampoline(namespace, efn, var); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.ident, var); let shim = expand_rust_function_shim_impl( @@ -385,7 +386,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type fn expand_rust_function_shim_impl( sig: &Signature, types: &Types, - link_name: &str, + link_name: &Symbol, local_name: Ident, catch_unwind_label: String, invoke: Option<&Ident>, diff --git a/syntax/mangle.rs b/syntax/mangle.rs index f2616d2..6f4e0d8 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,5 +1,7 @@ use crate::syntax::namespace::Namespace; -use crate::syntax::{symbol, ExternFn}; +use crate::syntax::symbol::{self, Symbol}; +use crate::syntax::ExternFn; +use proc_macro2::Ident; const CXXBRIDGE: &str = "cxxbridge02"; @@ -9,10 +11,19 @@ macro_rules! join { }; } -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String { +pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { match &efn.receiver { Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ident, efn.ident), None => join!(namespace, CXXBRIDGE, efn.ident), } - .to_string() +} + +// The C half of a function pointer trampoline. +pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(namespace, efn), var, 0) +} + +// The Rust half of a function pointer trampoline. +pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(namespace, efn), var, 1) } diff --git a/syntax/mod.rs b/syntax/mod.rs index 81b1a2f..49bb299 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -11,7 +11,7 @@ pub mod mangle; pub mod namespace; mod parse; pub mod set; -mod symbol; +pub mod symbol; mod tokens; pub mod types; diff --git a/syntax/symbol.rs b/syntax/symbol.rs index a40baaf..fa8e587 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -19,6 +19,12 @@ impl ToTokens for Symbol { } } +impl From<&Ident> for Symbol { + fn from(ident: &Ident) -> Self { + Symbol(ident.to_string()) + } +} + impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -37,7 +43,9 @@ pub trait Segment: Display { } impl Segment for str {} +impl Segment for usize {} impl Segment for Ident {} +impl Segment for Symbol {} impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { From e439c772aa9417615d4940b73dfcb1920dbcfef6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 07:24:58 +0000 Subject: [PATCH 304/2232] Consistently use "receiver" for method self type --- diff --git a/gen/write.rs b/gen/write.rs index c100691..05c646a 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -370,8 +370,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } let mangled = mangle::extern_fn(&out.namespace, efn); write!(out, "{}(", mangled); - if let Some(base) = &efn.receiver { - write!(out, "{} *self$", base.ident); + if let Some(receiver) = &efn.receiver { + write!(out, "{} *self$", receiver.ident); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -395,7 +395,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_return_type(out, &efn.ret); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident), - Some(base) => write!(out, "({}::*{}$)(", base.ident, efn.ident), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ident, efn.ident), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -412,7 +412,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " = "); match &efn.receiver { None => write!(out, "{}", efn.ident), - Some(base) => write!(out, "&{}::{}", base.ident, efn.ident), + Some(receiver) => write!(out, "&{}::{}", receiver.ident, efn.ident), } writeln!(out, ";"); write!(out, " "); @@ -534,8 +534,8 @@ fn write_rust_function_decl_impl( } write!(out, "{}(", link_name); let mut needs_comma = false; - if let Some(base) = &sig.receiver { - write!(out, "{} &self$", base.ident); + if let Some(receiver) = &sig.receiver { + write!(out, "{} &self$", receiver.ident); needs_comma = true; } for arg in &sig.args { @@ -580,8 +580,8 @@ fn write_rust_function_shim_decl( indirect_call: bool, ) { write_return_type(out, &sig.ret); - if let Some(base) = receiver { - write!(out, "{}::", base.ident); + if let Some(receiver) = receiver { + write!(out, "{}::", receiver.ident); } write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2155ba0..d6c0c83 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -126,9 +126,9 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let receiver = efn.receiver.iter().map(|base| { - let ident = &base.ident; - match base.mutability { + let receiver = efn.receiver.iter().map(|receiver| { + let ident = &receiver.ident; + match receiver.mutability { None => quote!(_: &#ident), Some(_) => quote!(_: &mut #ident), } @@ -169,10 +169,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); - let receiver = efn.receiver.iter().map(|base| match base.mutability { - None => quote!(&self), - Some(_) => quote!(&mut self), - }); + let receiver = efn + .receiver + .iter() + .map(|receiver| match receiver.mutability { + None => quote!(&self), + Some(_) => quote!(&mut self), + }); let args = efn.args.iter().map(|arg| quote!(#arg)); let all_args = receiver.chain(args); let ret = if efn.throws { @@ -292,8 +295,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } .unwrap_or(call); - let receiver_ident = efn.receiver.as_ref().map(|base| &base.ident); - match receiver_ident { + let receiver_type = efn.receiver.as_ref().map(|receiver| &receiver.ident); + match receiver_type { None => quote! { #doc pub fn #ident(#(#all_args,)*) #ret { @@ -307,9 +310,9 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } } }, - Some(base_ident) => quote! { + Some(receiver_type) => quote! { #doc - impl #base_ident { + impl #receiver_type { pub fn #ident(#(#all_args,)*) #ret { extern "C" { #decl @@ -391,9 +394,9 @@ fn expand_rust_function_shim_impl( catch_unwind_label: String, invoke: Option<&Ident>, ) -> TokenStream { - let receiver = sig.receiver.iter().map(|base| { - let ident = &base.ident; - match base.mutability { + let receiver = sig.receiver.iter().map(|receiver| { + let ident = &receiver.ident; + match receiver.mutability { None => quote!(__self: &#ident), Some(_) => quote!(__self: &mut #ident), } From 86710616bb628fa0b3d71921a1daeeb76f71a45e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 07:45:16 +0000 Subject: [PATCH 305/2232] Fix const in methods with shared reference receivers --- diff --git a/gen/write.rs b/gen/write.rs index 05c646a..631be52 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -371,6 +371,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { let mangled = mangle::extern_fn(&out.namespace, efn); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } write!(out, "{} *self$", receiver.ident); } for (i, arg) in efn.args.iter().enumerate() { @@ -535,6 +538,9 @@ fn write_rust_function_decl_impl( write!(out, "{}(", link_name); let mut needs_comma = false; if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } write!(out, "{} &self$", receiver.ident); needs_comma = true; } @@ -598,6 +604,11 @@ fn write_rust_function_shim_decl( write!(out, "void *extern$"); } write!(out, ")"); + if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } + } if !sig.throws { write!(out, " noexcept"); } From 439cde212949f6b15cd3f9d83febfd9051a22ea1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 07:46:25 +0000 Subject: [PATCH 306/2232] Unindent a large non-header codepath --- diff --git a/gen/write.rs b/gen/write.rs index 631be52..8f3ecc0 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -629,94 +629,94 @@ fn write_rust_function_shim_impl( write_rust_function_shim_decl(out, local_name, sig, sig.receiver.as_ref(), indirect_call); if out.header { writeln!(out, ";"); - } else { - writeln!(out, " {{"); - for arg in &sig.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - out.include.utility = true; - write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty); - writeln!(out, "> {}$(::std::move({0}));", arg.ident); - } + return; + } + writeln!(out, " {{"); + for arg in &sig.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; + write!(out, " ::rust::ManuallyDrop<"); + write_type(out, &arg.ty); + writeln!(out, "> {}$(::std::move({0}));", arg.ident); } + } + write!(out, " "); + let indirect_return = indirect_return(sig, types); + if indirect_return { + write!(out, "::rust::MaybeUninit<"); + write_type(out, sig.ret.as_ref().unwrap()); + writeln!(out, "> return$;"); write!(out, " "); - let indirect_return = indirect_return(sig, types); - if indirect_return { - write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap()); - writeln!(out, "> return$;"); - write!(out, " "); - } else if let Some(ret) = &sig.ret { - write!(out, "return "); - match ret { - Type::RustBox(_) => { - write_type(out, ret); - write!(out, "::from_raw("); - } - Type::UniquePtr(_) => { - write_type(out, ret); - write!(out, "("); - } - Type::Ref(_) => write!(out, "*"), - _ => {} - } - } - if sig.throws { - write!(out, "::rust::Str::Repr error$ = "); - } - write!(out, "{}(", invoke); - if sig.receiver.is_some() { - write!(out, "*this"); - } - for (i, arg) in sig.args.iter().enumerate() { - if i > 0 || sig.receiver.is_some() { - write!(out, ", "); - } - match &arg.ty { - Type::Str(_) => write!(out, "::rust::Str::Repr("), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), - ty if types.needs_indirect_abi(ty) => write!(out, "&"), - _ => {} + } else if let Some(ret) = &sig.ret { + write!(out, "return "); + match ret { + Type::RustBox(_) => { + write_type(out, ret); + write!(out, "::from_raw("); } - write!(out, "{}", arg.ident); - match &arg.ty { - Type::RustBox(_) => write!(out, ".into_raw()"), - Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), - ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), - _ => {} + Type::UniquePtr(_) => { + write_type(out, ret); + write!(out, "("); } + Type::Ref(_) => write!(out, "*"), + _ => {} } - if indirect_return { - if !sig.args.is_empty() { - write!(out, ", "); - } - write!(out, "&return$.value"); + } + if sig.throws { + write!(out, "::rust::Str::Repr error$ = "); + } + write!(out, "{}(", invoke); + if sig.receiver.is_some() { + write!(out, "*this"); + } + for (i, arg) in sig.args.iter().enumerate() { + if i > 0 || sig.receiver.is_some() { + write!(out, ", "); } - if indirect_call { - if !sig.args.is_empty() || indirect_return { - write!(out, ", "); - } - write!(out, "extern$"); + match &arg.ty { + Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), + ty if types.needs_indirect_abi(ty) => write!(out, "&"), + _ => {} } - write!(out, ")"); - if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { - write!(out, ")"); - } + write!(out, "{}", arg.ident); + match &arg.ty { + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::UniquePtr(_) => write!(out, ".release()"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), + ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), + _ => {} } - writeln!(out, ";"); - if sig.throws { - writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::Error(error$);"); - writeln!(out, " }}"); + } + if indirect_return { + if !sig.args.is_empty() { + write!(out, ", "); } - if indirect_return { - out.include.utility = true; - writeln!(out, " return ::std::move(return$.value);"); + write!(out, "&return$.value"); + } + if indirect_call { + if !sig.args.is_empty() || indirect_return { + write!(out, ", "); } - writeln!(out, "}}"); + write!(out, "extern$"); } + write!(out, ")"); + if let Some(ret) = &sig.ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + write!(out, ")"); + } + } + writeln!(out, ";"); + if sig.throws { + writeln!(out, " if (error$.ptr) {{"); + writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " }}"); + } + if indirect_return { + out.include.utility = true; + writeln!(out, " return ::std::move(return$.value);"); + } + writeln!(out, "}}"); } fn write_return_type(out: &mut OutFile, ty: &Option) { From c3f485c54bf8f19e924f2f855a2924e8a783c5e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 07:52:05 +0000 Subject: [PATCH 307/2232] Simplify variable name of receiver in C++ shims Even without $, `self` can't collide with any of the variable names the user has given to the other function parameters. --- diff --git a/gen/write.rs b/gen/write.rs index 8f3ecc0..9ab6e38 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -374,7 +374,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} *self$", receiver.ident); + write!(out, "{} *self", receiver.ident); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -442,7 +442,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } match &efn.receiver { None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self$->*{}$)(", efn.ident), + Some(_) => write!(out, "(self->*{}$)(", efn.ident), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -541,7 +541,7 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self$", receiver.ident); + write!(out, "{} &self", receiver.ident); needs_comma = true; } for arg in &sig.args { From 41909e69d45789ce259f65d7de709cfc7c55273a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 07:55:15 +0000 Subject: [PATCH 308/2232] Consistently use C++ reference for receiver arguments --- diff --git a/gen/write.rs b/gen/write.rs index 9ab6e38..f7c4a19 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -374,7 +374,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} *self", receiver.ident); + write!(out, "{} &self", receiver.ident); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -442,7 +442,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } match &efn.receiver { None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self->*{}$)(", efn.ident), + Some(_) => write!(out, "(self.*{}$)(", efn.ident), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { From a73853b521c59f867f34da4541e0940c4ce61961 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 08:20:05 +0000 Subject: [PATCH 309/2232] Fold qualified path into local_name --- diff --git a/gen/write.rs b/gen/write.rs index f7c4a19..2cb8b51 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -3,9 +3,7 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{ - mangle, Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var, -}; +use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -335,8 +333,8 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = Symbol::from(&method.ident); - write_rust_function_shim_decl(out, &local_name, sig, None, false); + let local_name = method.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } writeln!(out, "}};"); @@ -513,7 +511,7 @@ fn write_function_pointer_trampoline( write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var); + let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } @@ -572,7 +570,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } - let local_name = Symbol::from(&efn.ident); + let local_name = match &efn.sig.receiver { + None => efn.ident.to_string(), + Some(receiver) => format!("{}::{}", receiver.ident, efn.ident), + }; let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); @@ -580,15 +581,11 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { fn write_rust_function_shim_decl( out: &mut OutFile, - local_name: &Symbol, + local_name: &str, sig: &Signature, - receiver: Option<&Receiver>, indirect_call: bool, ) { write_return_type(out, &sig.ret); - if let Some(receiver) = receiver { - write!(out, "{}::", receiver.ident); - } write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { @@ -616,7 +613,7 @@ fn write_rust_function_shim_decl( fn write_rust_function_shim_impl( out: &mut OutFile, - local_name: &Symbol, + local_name: &str, sig: &Signature, types: &Types, invoke: &Symbol, @@ -626,7 +623,7 @@ fn write_rust_function_shim_impl( // We've already defined this inside the struct. return; } - write_rust_function_shim_decl(out, local_name, sig, sig.receiver.as_ref(), indirect_call); + write_rust_function_shim_decl(out, local_name, sig, indirect_call); if out.header { writeln!(out, ";"); return; From fb6e386cf40a36f4064358bf441b3fe5686dc326 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 08:35:08 +0000 Subject: [PATCH 310/2232] Clean up printing of Receiver to tokens --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d6c0c83..91208e6 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -126,13 +126,7 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let receiver = efn.receiver.iter().map(|receiver| { - let ident = &receiver.ident; - match receiver.mutability { - None => quote!(_: &#ident), - Some(_) => quote!(_: &mut #ident), - } - }); + let receiver = efn.receiver.iter().map(|receiver| quote!(_: #receiver)); let args = efn.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -169,13 +163,10 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); - let receiver = efn - .receiver - .iter() - .map(|receiver| match receiver.mutability { - None => quote!(&self), - Some(_) => quote!(&mut self), - }); + let receiver = efn.receiver.iter().map(|receiver| { + let mutability = receiver.mutability; + quote!(&#mutability self) + }); let args = efn.args.iter().map(|arg| quote!(#arg)); let all_args = receiver.chain(args); let ret = if efn.throws { @@ -394,13 +385,10 @@ fn expand_rust_function_shim_impl( catch_unwind_label: String, invoke: Option<&Ident>, ) -> TokenStream { - let receiver = sig.receiver.iter().map(|receiver| { - let ident = &receiver.ident; - match receiver.mutability { - None => quote!(__self: &#ident), - Some(_) => quote!(__self: &mut #ident), - } - }); + let receiver = sig + .receiver + .iter() + .map(|receiver| quote!(__self: #receiver)); let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); diff --git a/syntax/impls.rs b/syntax/impls.rs index d3c4b0f..3509899 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -184,8 +184,13 @@ impl Eq for Receiver {} impl PartialEq for Receiver { fn eq(&self, other: &Receiver) -> bool { - let Receiver { mutability, ident } = self; let Receiver { + ampersand: _, + mutability, + ident, + } = self; + let Receiver { + ampersand: _, mutability: mutability2, ident: ident2, } = other; @@ -195,7 +200,11 @@ impl PartialEq for Receiver { impl Hash for Receiver { fn hash(&self, state: &mut H) { - let Receiver { mutability, ident } = self; + let Receiver { + ampersand: _, + mutability, + ident, + } = self; mutability.is_some().hash(state); ident.hash(state); } diff --git a/syntax/mod.rs b/syntax/mod.rs index 49bb299..b160e5a 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -75,6 +75,7 @@ pub struct Var { } pub struct Receiver { + pub ampersand: Token![&], pub mutability: Option, pub ident: Ident, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 6e5b36e..7bd36de 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -180,6 +180,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { if let Type::Ref(reference) = ty { if let Type::Ident(ident) = reference.inner { receiver = Some(Receiver { + ampersand: reference.ampersand, mutability: reference.mutability, ident, }); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index df11852..784de37 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Ref, Signature, Slice, Ty1, Type, Var}; +use crate::syntax::{Derive, ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type, Var}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::Token; @@ -97,3 +97,11 @@ impl ToTokens for Signature { } } } + +impl ToTokens for Receiver { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.ampersand.to_tokens(tokens); + self.mutability.to_tokens(tokens); + self.ident.to_tokens(tokens); + } +} From 25ca093e23ff32a9900c07d29decd0d8c3d5bf19 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 08:36:43 +0000 Subject: [PATCH 311/2232] Fix placement of doc comment on c++ method shim --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 91208e6..1bc2fd1 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -302,8 +302,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } }, Some(receiver_type) => quote! { - #doc impl #receiver_type { + #doc pub fn #ident(#(#all_args,)*) #ret { extern "C" { #decl From c66cdbb57f19212d1e0b72454901471a98d1485a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 08:41:15 +0000 Subject: [PATCH 312/2232] Reduce duplication of function vs method c++ function shim --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1bc2fd1..acc74d5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -286,36 +286,25 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } .unwrap_or(call); - let receiver_type = efn.receiver.as_ref().map(|receiver| &receiver.ident); - match receiver_type { - None => quote! { - #doc - pub fn #ident(#(#all_args,)*) #ret { - extern "C" { - #decl - } - #trampolines - unsafe { - #setup - #expr - } + let function_shim = quote! { + #doc + pub fn #ident(#(#all_args,)*) #ret { + extern "C" { + #decl } - }, - Some(receiver_type) => quote! { - impl #receiver_type { - #doc - pub fn #ident(#(#all_args,)*) #ret { - extern "C" { - #decl - } - #trampolines - unsafe { - #setup - #expr - } - } + #trampolines + unsafe { + #setup + #expr } - }, + } + }; + match &efn.receiver { + None => function_shim, + Some(receiver) => { + let receiver_type = &receiver.ident; + quote!(impl #receiver_type { #function_shim }) + } } } From 3a45f2d76eab423bb6f2ecef41d15d5a7cd43479 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 09:09:38 +0000 Subject: [PATCH 313/2232] Avoid autoref/deref in method shims --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index acc74d5..207682e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -389,7 +389,8 @@ fn expand_rust_function_shim_impl( }); let all_args = receiver.chain(args); - let vars = sig.args.iter().map(|arg| { + let receiver_var = sig.receiver.iter().map(|_| quote!(__self)); + let arg_vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { Type::Ident(i) if i == RustString => { @@ -407,11 +408,15 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), } }); + let vars = receiver_var.chain(arg_vars); let mut call = match invoke { - Some(ident) => match sig.receiver { + Some(ident) => match &sig.receiver { None => quote!(super::#ident), - Some(_) => quote!(__self.#ident), + Some(receiver) => { + let receiver_type = &receiver.ident; + quote!(#receiver_type::#ident) + } }, None => quote!(__extern), }; From 05e11cca1f6c2475f8f52f37155ba8de06ab5988 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 09:13:56 +0000 Subject: [PATCH 314/2232] Preserve span of self var in Receiver --- diff --git a/gen/write.rs b/gen/write.rs index 2cb8b51..a8e3fd4 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -51,7 +51,7 @@ pub(super) fn gen( if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type - .entry(&receiver.ident) + .entry(&receiver.ty) .or_insert_with(Vec::new) .push(efn); } @@ -372,7 +372,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ident); + write!(out, "{} &self", receiver.ty); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -396,7 +396,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write_return_type(out, &efn.ret); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ident, efn.ident), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -413,7 +413,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " = "); match &efn.receiver { None => write!(out, "{}", efn.ident), - Some(receiver) => write!(out, "&{}::{}", receiver.ident, efn.ident), + Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), } writeln!(out, ";"); write!(out, " "); @@ -539,7 +539,7 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ident); + write!(out, "{} &self", receiver.ty); needs_comma = true; } for arg in &sig.args { @@ -572,7 +572,7 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } let local_name = match &efn.sig.receiver { None => efn.ident.to_string(), - Some(receiver) => format!("{}::{}", receiver.ident, efn.ident), + Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), }; let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 207682e..4841794 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -302,7 +302,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types match &efn.receiver { None => function_shim, Some(receiver) => { - let receiver_type = &receiver.ident; + let receiver_type = &receiver.ty; quote!(impl #receiver_type { #function_shim }) } } @@ -414,7 +414,7 @@ fn expand_rust_function_shim_impl( Some(ident) => match &sig.receiver { None => quote!(super::#ident), Some(receiver) => { - let receiver_type = &receiver.ident; + let receiver_type = &receiver.ty; quote!(#receiver_type::#ident) } }, diff --git a/syntax/impls.rs b/syntax/impls.rs index 3509899..7b2d746 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -187,14 +187,16 @@ impl PartialEq for Receiver { let Receiver { ampersand: _, mutability, - ident, + var: _, + ty, } = self; let Receiver { ampersand: _, mutability: mutability2, - ident: ident2, + var: _, + ty: ty2, } = other; - mutability.is_some() == mutability2.is_some() && ident == ident2 + mutability.is_some() == mutability2.is_some() && ty == ty2 } } @@ -203,9 +205,10 @@ impl Hash for Receiver { let Receiver { ampersand: _, mutability, - ident, + var: _, + ty, } = self; mutability.is_some().hash(state); - ident.hash(state); + ty.hash(state); } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 6f4e0d8..1380704 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -13,7 +13,7 @@ macro_rules! join { pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ident, efn.ident), + Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident), None => join!(namespace, CXXBRIDGE, efn.ident), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index b160e5a..3a2bb93 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -77,7 +77,8 @@ pub struct Var { pub struct Receiver { pub ampersand: Token![&], pub mutability: Option, - pub ident: Ident, + pub var: Token![self], + pub ty: Ident, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index 7bd36de..d8bc0e5 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -182,7 +182,8 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { receiver = Some(Receiver { ampersand: reference.ampersand, mutability: reference.mutability, - ident, + var: Token![self](ident.span()), + ty: ident, }); continue; } diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 784de37..6e3db9b 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -102,6 +102,6 @@ impl ToTokens for Receiver { fn to_tokens(&self, tokens: &mut TokenStream) { self.ampersand.to_tokens(tokens); self.mutability.to_tokens(tokens); - self.ident.to_tokens(tokens); + self.ty.to_tokens(tokens); } } From f9ffb93365e0511fb4e66315809e110f7b1e6b32 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 09:23:14 +0000 Subject: [PATCH 315/2232] Preserve all the spans when manipulating receiver --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4841794..350ee97 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -5,7 +5,7 @@ use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; -use quote::{format_ident, quote, quote_spanned}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { @@ -164,8 +164,10 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); let receiver = efn.receiver.iter().map(|receiver| { + let ampersand = receiver.ampersand; let mutability = receiver.mutability; - quote!(&#mutability self) + let var = receiver.var; + quote!(#ampersand #mutability #var) }); let args = efn.args.iter().map(|arg| quote!(#arg)); let all_args = receiver.chain(args); @@ -179,7 +181,10 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types expand_return_type(&efn.ret) }; let indirect_return = indirect_return(efn, types); - let receiver_var = efn.receiver.iter().map(|_| quote!(self)); + let receiver_var = efn + .receiver + .iter() + .map(|receiver| receiver.var.to_token_stream()); let arg_vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { @@ -374,10 +379,14 @@ fn expand_rust_function_shim_impl( catch_unwind_label: String, invoke: Option<&Ident>, ) -> TokenStream { + let receiver_var = sig + .receiver + .as_ref() + .map(|receiver| quote_spanned!(receiver.var.span=> __self)); let receiver = sig .receiver - .iter() - .map(|receiver| quote!(__self: #receiver)); + .as_ref() + .map(|receiver| quote!(#receiver_var: #receiver)); let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -387,9 +396,8 @@ fn expand_rust_function_shim_impl( quote!(#ident: #ty) } }); - let all_args = receiver.chain(args); + let all_args = receiver.into_iter().chain(args); - let receiver_var = sig.receiver.iter().map(|_| quote!(__self)); let arg_vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { @@ -408,7 +416,7 @@ fn expand_rust_function_shim_impl( _ => quote!(#ident), } }); - let vars = receiver_var.chain(arg_vars); + let vars = receiver_var.into_iter().chain(arg_vars); let mut call = match invoke { Some(ident) => match &sig.receiver { From 16b9068d7b9a41d61e95d472abea7af3ba2cbb24 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 20:26:16 +0000 Subject: [PATCH 316/2232] Bump bazel rules_rust to pull in optional extra_target_triples --- diff --git a/WORKSPACE b/WORKSPACE index f39808c..501bde0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "abc75a5b6c8eda46a3d141921841e3577e9707b32d4d5b5cc156f7b8b28631ad", - strip_prefix = "rules_rust-d97f99628439df8bec89f5b7bc439f9d43d1586b", + sha256 = "b83154a58f95618e06845b774b079000e0c39830e185db4c7bf46e79896cb3a1", + strip_prefix = "rules_rust-0deef6dd8180cd3bc610878558bb26921b4e8de1", # Master branch as of 2020-03-07 - url = "https://github.com/bazelbuild/rules_rust/archive/d97f99628439df8bec89f5b7bc439f9d43d1586b.tar.gz", + url = "https://github.com/bazelbuild/rules_rust/archive/0deef6dd8180cd3bc610878558bb26921b4e8de1.tar.gz", ) http_archive( @@ -26,7 +26,6 @@ load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( name = "rust_1_43_beta_linux", exec_triple = "x86_64-unknown-linux-gnu", - extra_target_triples = [], iso_date = "2020-04-19", version = "beta", ) @@ -34,7 +33,6 @@ rust_repository_set( rust_repository_set( name = "rust_1_43_beta_darwin", exec_triple = "x86_64-apple-darwin", - extra_target_triples = [], iso_date = "2020-04-19", version = "beta", ) From ce855186ae3de12ea08b5a6cb95aa0e9553a44d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 20 2020 20:38:20 +0000 Subject: [PATCH 317/2232] Merge pull request #129 from dtolnay/bazel Bump bazel rules_rust to pull in optional extra_target_triples --- diff --git a/WORKSPACE b/WORKSPACE index f39808c..501bde0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "abc75a5b6c8eda46a3d141921841e3577e9707b32d4d5b5cc156f7b8b28631ad", - strip_prefix = "rules_rust-d97f99628439df8bec89f5b7bc439f9d43d1586b", + sha256 = "b83154a58f95618e06845b774b079000e0c39830e185db4c7bf46e79896cb3a1", + strip_prefix = "rules_rust-0deef6dd8180cd3bc610878558bb26921b4e8de1", # Master branch as of 2020-03-07 - url = "https://github.com/bazelbuild/rules_rust/archive/d97f99628439df8bec89f5b7bc439f9d43d1586b.tar.gz", + url = "https://github.com/bazelbuild/rules_rust/archive/0deef6dd8180cd3bc610878558bb26921b4e8de1.tar.gz", ) http_archive( @@ -26,7 +26,6 @@ load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( name = "rust_1_43_beta_linux", exec_triple = "x86_64-unknown-linux-gnu", - extra_target_triples = [], iso_date = "2020-04-19", version = "beta", ) @@ -34,7 +33,6 @@ rust_repository_set( rust_repository_set( name = "rust_1_43_beta_darwin", exec_triple = "x86_64-apple-darwin", - extra_target_triples = [], iso_date = "2020-04-19", version = "beta", ) From e1e969d06f092428ff8d82d3c262c326b0ecc5bf Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 22 2020 15:44:49 +0000 Subject: [PATCH 318/2232] Allow &self without a type when the block only has one type --- diff --git a/syntax/parse.rs b/syntax/parse.rs index d8bc0e5..4e78588 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -91,16 +91,28 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { Lang::Rust => Api::RustFunction, }; - let mut items = Vec::new(); + let mut types = Vec::new(); for foreign in &foreign_mod.items { match foreign { ForeignItem::Type(foreign) => { check_reserved_name(&foreign.ident)?; let ety = parse_extern_type(foreign)?; - items.push(api_type(ety)); + types.push(ety); } + _ => {} + } + } + let single_type = if types.len() == 1 { + Some(&types[0]) + } else { + None + }; + let mut items = Vec::new(); + for foreign in &foreign_mod.items { + match foreign { + ForeignItem::Type(_) => {} ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang)?; + let efn = parse_extern_fn(foreign, lang, &single_type)?; items.push(api_function(efn)); } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -110,6 +122,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), } } + items.extend(types.into_iter().map(|ety| api_type(ety))); Ok(items) } @@ -141,7 +154,11 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { }) } -fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + foreign_fn: &ForeignItemFn, + lang: Lang, + single_type: &Option<&ExternType>, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -161,8 +178,19 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { for arg in foreign_fn.sig.inputs.pairs() { let (arg, comma) = arg.into_tuple(); match arg { - FnArg::Receiver(receiver) => { - return Err(Error::new_spanned(receiver, "unsupported signature")) + FnArg::Receiver(rcvr) => { + if let Some(ety) = single_type { + if let Some((and, _)) = rcvr.reference { + receiver = Some(Receiver { + ampersand: and, + mutability: rcvr.mutability, + var: Token![self](ety.ident.span()), + ty: ety.ident.clone(), + }); + continue; + } + } + return Err(Error::new_spanned(rcvr, "unsupported signature")); } FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4da5740..efdd1fa 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -52,6 +52,8 @@ pub mod ffi { fn get(self: &C) -> usize; fn set(self: &mut C, n: usize) -> usize; + fn get2(&self) -> usize; + fn set2(&mut self, n: usize) -> usize; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8893ee8..619485a 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -15,11 +15,18 @@ C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } +size_t C::get2() const { return this->n; } + size_t C::set(size_t n) { this->n = n; return this->n; } +size_t C::set2(size_t n) { + this->n = n; + return this->n; +} + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 43ac229..ed6d541 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -13,6 +13,8 @@ public: C(size_t n); size_t get() const; size_t set(size_t n); + size_t get2() const; + size_t set2(size_t n); private: size_t n; diff --git a/tests/test.rs b/tests/test.rs index eb14d36..1cd85df 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -102,8 +102,8 @@ fn test_c_method_calls() { assert_eq!(2020, old_value); assert_eq!(2021, unique_ptr.set(2021)); assert_eq!(2021, unique_ptr.get()); - assert_eq!(old_value, unique_ptr.set(old_value)); - assert_eq!(old_value, unique_ptr.get()) + assert_eq!(old_value, unique_ptr.set2(old_value)); + assert_eq!(old_value, unique_ptr.get2()) } #[no_mangle] From c0a51946c082052738e218297a4aa6eb87a6d41c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 19:55:56 +0000 Subject: [PATCH 319/2232] Merge pull request #131 from jgalenson/methods Allow &self without a type when the block only has one type --- diff --git a/syntax/parse.rs b/syntax/parse.rs index d8bc0e5..4e78588 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -91,16 +91,28 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { Lang::Rust => Api::RustFunction, }; - let mut items = Vec::new(); + let mut types = Vec::new(); for foreign in &foreign_mod.items { match foreign { ForeignItem::Type(foreign) => { check_reserved_name(&foreign.ident)?; let ety = parse_extern_type(foreign)?; - items.push(api_type(ety)); + types.push(ety); } + _ => {} + } + } + let single_type = if types.len() == 1 { + Some(&types[0]) + } else { + None + }; + let mut items = Vec::new(); + for foreign in &foreign_mod.items { + match foreign { + ForeignItem::Type(_) => {} ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang)?; + let efn = parse_extern_fn(foreign, lang, &single_type)?; items.push(api_function(efn)); } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -110,6 +122,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), } } + items.extend(types.into_iter().map(|ety| api_type(ety))); Ok(items) } @@ -141,7 +154,11 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { }) } -fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + foreign_fn: &ForeignItemFn, + lang: Lang, + single_type: &Option<&ExternType>, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -161,8 +178,19 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { for arg in foreign_fn.sig.inputs.pairs() { let (arg, comma) = arg.into_tuple(); match arg { - FnArg::Receiver(receiver) => { - return Err(Error::new_spanned(receiver, "unsupported signature")) + FnArg::Receiver(rcvr) => { + if let Some(ety) = single_type { + if let Some((and, _)) = rcvr.reference { + receiver = Some(Receiver { + ampersand: and, + mutability: rcvr.mutability, + var: Token![self](ety.ident.span()), + ty: ety.ident.clone(), + }); + continue; + } + } + return Err(Error::new_spanned(rcvr, "unsupported signature")); } FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4da5740..efdd1fa 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -52,6 +52,8 @@ pub mod ffi { fn get(self: &C) -> usize; fn set(self: &mut C, n: usize) -> usize; + fn get2(&self) -> usize; + fn set2(&mut self, n: usize) -> usize; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8893ee8..619485a 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -15,11 +15,18 @@ C::C(size_t n) : n(n) {} size_t C::get() const { return this->n; } +size_t C::get2() const { return this->n; } + size_t C::set(size_t n) { this->n = n; return this->n; } +size_t C::set2(size_t n) { + this->n = n; + return this->n; +} + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 43ac229..ed6d541 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -13,6 +13,8 @@ public: C(size_t n); size_t get() const; size_t set(size_t n); + size_t get2() const; + size_t set2(size_t n); private: size_t n; diff --git a/tests/test.rs b/tests/test.rs index eb14d36..1cd85df 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -102,8 +102,8 @@ fn test_c_method_calls() { assert_eq!(2020, old_value); assert_eq!(2021, unique_ptr.set(2021)); assert_eq!(2021, unique_ptr.get()); - assert_eq!(old_value, unique_ptr.set(old_value)); - assert_eq!(old_value, unique_ptr.get()) + assert_eq!(old_value, unique_ptr.set2(old_value)); + assert_eq!(old_value, unique_ptr.get2()) } #[no_mangle] From 1dd11a16b4505f30933a96d7f4185780164197fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 22:33:54 +0000 Subject: [PATCH 320/2232] Touch up &self shorthand PR --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 4e78588..8cc4e2a 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -93,13 +93,10 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let mut types = Vec::new(); for foreign in &foreign_mod.items { - match foreign { - ForeignItem::Type(foreign) => { - check_reserved_name(&foreign.ident)?; - let ety = parse_extern_type(foreign)?; - types.push(ety); - } - _ => {} + if let ForeignItem::Type(foreign) = foreign { + check_reserved_name(&foreign.ident)?; + let ety = parse_extern_type(foreign)?; + types.push(ety); } } let single_type = if types.len() == 1 { @@ -178,19 +175,19 @@ fn parse_extern_fn( for arg in foreign_fn.sig.inputs.pairs() { let (arg, comma) = arg.into_tuple(); match arg { - FnArg::Receiver(rcvr) => { + FnArg::Receiver(arg) => { if let Some(ety) = single_type { - if let Some((and, _)) = rcvr.reference { + if let Some((ampersand, _)) = arg.reference { receiver = Some(Receiver { - ampersand: and, - mutability: rcvr.mutability, + ampersand, + mutability: arg.mutability, var: Token![self](ety.ident.span()), ty: ety.ident.clone(), }); continue; } } - return Err(Error::new_spanned(rcvr, "unsupported signature")); + return Err(Error::new_spanned(arg, "unsupported signature")); } FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { From 18ba92ce821ff9935bb42cdf74defd9909940e63 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:22:47 +0000 Subject: [PATCH 321/2232] Split Receiver's ToTokens into a wrapper type --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 350ee97..36f25c5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -126,7 +126,10 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream { fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; - let receiver = efn.receiver.iter().map(|receiver| quote!(_: #receiver)); + let receiver = efn.receiver.iter().map(|receiver| { + let receiver_type = receiver.ty(); + quote!(_: #receiver_type) + }); let args = efn.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); @@ -383,10 +386,10 @@ fn expand_rust_function_shim_impl( .receiver .as_ref() .map(|receiver| quote_spanned!(receiver.var.span=> __self)); - let receiver = sig - .receiver - .as_ref() - .map(|receiver| quote!(#receiver_var: #receiver)); + let receiver = sig.receiver.as_ref().map(|receiver| { + let receiver_type = receiver.ty(); + quote!(#receiver_var: #receiver_type) + }); let args = sig.args.iter().map(|arg| { let ident = &arg.ident; let ty = expand_extern_type(&arg.ty); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 6e3db9b..08c966a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -98,10 +98,19 @@ impl ToTokens for Signature { } } -impl ToTokens for Receiver { +pub struct ReceiverType<'a>(&'a Receiver); + +impl Receiver { + // &TheType + pub fn ty(&self) -> ReceiverType { + ReceiverType(self) + } +} + +impl ToTokens for ReceiverType<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { - self.ampersand.to_tokens(tokens); - self.mutability.to_tokens(tokens); - self.ty.to_tokens(tokens); + self.0.ampersand.to_tokens(tokens); + self.0.mutability.to_tokens(tokens); + self.0.ty.to_tokens(tokens); } } From 0bd50fa93139da55d2c0b13aaa604805e0da2e76 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:23:39 +0000 Subject: [PATCH 322/2232] Preserve lifetimes on parsed references --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 7b2d746..657fdb3 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -86,15 +86,17 @@ impl PartialEq for Ref { fn eq(&self, other: &Ref) -> bool { let Ref { ampersand: _, + lifetime, mutability, inner, } = self; let Ref { ampersand: _, + lifetime: lifetime2, mutability: mutability2, inner: inner2, } = other; - mutability.is_some() == mutability2.is_some() && inner == inner2 + lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && inner == inner2 } } @@ -102,9 +104,11 @@ impl Hash for Ref { fn hash(&self, state: &mut H) { let Ref { ampersand: _, + lifetime, mutability, inner, } = self; + lifetime.hash(state); mutability.is_some().hash(state); inner.hash(state); } @@ -186,17 +190,19 @@ impl PartialEq for Receiver { fn eq(&self, other: &Receiver) -> bool { let Receiver { ampersand: _, + lifetime, mutability, var: _, ty, } = self; let Receiver { ampersand: _, + lifetime: lifetime2, mutability: mutability2, var: _, ty: ty2, } = other; - mutability.is_some() == mutability2.is_some() && ty == ty2 + lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && ty == ty2 } } @@ -204,10 +210,12 @@ impl Hash for Receiver { fn hash(&self, state: &mut H) { let Receiver { ampersand: _, + lifetime, mutability, var: _, ty, } = self; + lifetime.hash(state); mutability.is_some().hash(state); ty.hash(state); } diff --git a/syntax/mod.rs b/syntax/mod.rs index 3a2bb93..cf811cf 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -19,7 +19,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{LitStr, Token}; +use syn::{Lifetime, LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -76,6 +76,7 @@ pub struct Var { pub struct Receiver { pub ampersand: Token![&], + pub lifetime: Option, pub mutability: Option, pub var: Token![self], pub ty: Ident, @@ -102,6 +103,7 @@ pub struct Ty1 { pub struct Ref { pub ampersand: Token![&], + pub lifetime: Option, pub mutability: Option, pub inner: Type, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 8cc4e2a..aed77d8 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -177,9 +177,10 @@ fn parse_extern_fn( match arg { FnArg::Receiver(arg) => { if let Some(ety) = single_type { - if let Some((ampersand, _)) = arg.reference { + if let Some((ampersand, lifetime)) = &arg.reference { receiver = Some(Receiver { - ampersand, + ampersand: *ampersand, + lifetime: lifetime.clone(), mutability: arg.mutability, var: Token![self](ety.ident.span()), ty: ety.ident.clone(), @@ -206,6 +207,7 @@ fn parse_extern_fn( if let Type::Ident(ident) = reference.inner { receiver = Some(Receiver { ampersand: reference.ampersand, + lifetime: reference.lifetime, mutability: reference.mutability, var: Token![self](ident.span()), ty: ident, @@ -273,6 +275,7 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }; Ok(which(Box::new(Ref { ampersand: ty.and_token, + lifetime: ty.lifetime.clone(), mutability: ty.mutability, inner, }))) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 08c966a..26bb3d1 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -47,6 +47,7 @@ impl ToTokens for Ty1 { impl ToTokens for Ref { fn to_tokens(&self, tokens: &mut TokenStream) { self.ampersand.to_tokens(tokens); + self.lifetime.to_tokens(tokens); self.mutability.to_tokens(tokens); self.inner.to_tokens(tokens); } @@ -110,6 +111,7 @@ impl Receiver { impl ToTokens for ReceiverType<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { self.0.ampersand.to_tokens(tokens); + self.0.lifetime.to_tokens(tokens); self.0.mutability.to_tokens(tokens); self.0.ty.to_tokens(tokens); } From 62d360ccaf3b73ec145adebee53a5d7b227560cf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:26:21 +0000 Subject: [PATCH 323/2232] Preserve whether Receiver was shorthand for error reporting --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 657fdb3..acc99ab 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -194,6 +194,7 @@ impl PartialEq for Receiver { mutability, var: _, ty, + shorthand: _, } = self; let Receiver { ampersand: _, @@ -201,6 +202,7 @@ impl PartialEq for Receiver { mutability: mutability2, var: _, ty: ty2, + shorthand: _, } = other; lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && ty == ty2 } @@ -214,6 +216,7 @@ impl Hash for Receiver { mutability, var: _, ty, + shorthand: _, } = self; lifetime.hash(state); mutability.is_some().hash(state); diff --git a/syntax/mod.rs b/syntax/mod.rs index cf811cf..ab5cecc 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -80,6 +80,7 @@ pub struct Receiver { pub mutability: Option, pub var: Token![self], pub ty: Ident, + pub shorthand: bool, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index aed77d8..71e0c76 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -184,6 +184,7 @@ fn parse_extern_fn( mutability: arg.mutability, var: Token![self](ety.ident.span()), ty: ety.ident.clone(), + shorthand: true, }); continue; } @@ -211,6 +212,7 @@ fn parse_extern_fn( mutability: reference.mutability, var: Token![self](ident.span()), ty: ident, + shorthand: false, }); continue; } From bfad5f759fbdcbce1c28b66716e9e44cd1101c3a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:36:55 +0000 Subject: [PATCH 324/2232] Preserve span on Receiver shorthand `self` token --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 71e0c76..bdccbda 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -182,7 +182,7 @@ fn parse_extern_fn( ampersand: *ampersand, lifetime: lifetime.clone(), mutability: arg.mutability, - var: Token![self](ety.ident.span()), + var: arg.self_token, ty: ety.ident.clone(), shorthand: true, }); From d763f4cd6457a3b0ec061b56b2af6b6def2f0907 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:39:44 +0000 Subject: [PATCH 325/2232] Reject explicit lifetimes in a reference type --- diff --git a/syntax/check.rs b/syntax/check.rs index 3b6937a..fa51ed3 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Slice, Struct, Ty1, Type, Types}; +use crate::syntax::{ + error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, +}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; @@ -100,6 +102,10 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { } fn check_type_ref(cx: &mut Check, ty: &Ref) { + if ty.lifetime.is_some() { + cx.error(ty, "references with explicit lifetimes are not supported"); + } + match ty.inner { Type::Fn(_) | Type::Void(_) => {} _ => return, @@ -134,6 +140,13 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } fn check_api_fn(cx: &mut Check, efn: &ExternFn) { + if let Some(receiver) = &efn.receiver { + if receiver.lifetime.is_some() { + let span = span_for_receiver_error(receiver); + cx.error(span, "references with explicit lifetimes are not supported"); + } + } + for arg in &efn.args { if is_unsized(cx, &arg.ty) { let desc = describe(cx, &arg.ty); @@ -223,6 +236,19 @@ fn span_for_struct_error(strct: &Struct) -> TokenStream { quote!(#struct_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 combine_errors(errors: Vec) -> Result<()> { let mut iter = errors.into_iter(); let mut all_errors = match iter.next() { From 6d9f4aab20245ac30ea1e222f268833ddd5d2284 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:39:44 +0000 Subject: [PATCH 326/2232] Add ui test for disallowed lifetimes --- diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs new file mode 100644 index 0000000..ee39fef --- /dev/null +++ b/tests/ui/disallow_lifetime.rs @@ -0,0 +1,13 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type C; + fn f(&'static self); + } + + extern "Rust" { + fn f(string: &'a String); + } +} + +fn main() {} diff --git a/tests/ui/disallow_lifetime.stderr b/tests/ui/disallow_lifetime.stderr new file mode 100644 index 0000000..d7c42e4 --- /dev/null +++ b/tests/ui/disallow_lifetime.stderr @@ -0,0 +1,11 @@ +error: references with explicit lifetimes are not supported + --> $DIR/disallow_lifetime.rs:9:22 + | +9 | fn f(string: &'a String); + | ^^^^^^^^^^ + +error: references with explicit lifetimes are not supported + --> $DIR/disallow_lifetime.rs:5:14 + | +5 | fn f(&'static self); + | ^^^^^^^^^^^^^ From 3273a5fb9b966d164055cdf757addb1dfc8c97ac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:51:43 +0000 Subject: [PATCH 327/2232] Merge pull request #133 from dtolnay/lifetimes Trigger error on explicitly specified lifetimes in a reference type --- diff --git a/syntax/check.rs b/syntax/check.rs index 3b6937a..fa51ed3 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::{error, ident, Api, ExternFn, Lang, Ref, Slice, Struct, Ty1, Type, Types}; +use crate::syntax::{ + error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, +}; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; @@ -100,6 +102,10 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { } fn check_type_ref(cx: &mut Check, ty: &Ref) { + if ty.lifetime.is_some() { + cx.error(ty, "references with explicit lifetimes are not supported"); + } + match ty.inner { Type::Fn(_) | Type::Void(_) => {} _ => return, @@ -134,6 +140,13 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } fn check_api_fn(cx: &mut Check, efn: &ExternFn) { + if let Some(receiver) = &efn.receiver { + if receiver.lifetime.is_some() { + let span = span_for_receiver_error(receiver); + cx.error(span, "references with explicit lifetimes are not supported"); + } + } + for arg in &efn.args { if is_unsized(cx, &arg.ty) { let desc = describe(cx, &arg.ty); @@ -223,6 +236,19 @@ fn span_for_struct_error(strct: &Struct) -> TokenStream { quote!(#struct_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 combine_errors(errors: Vec) -> Result<()> { let mut iter = errors.into_iter(); let mut all_errors = match iter.next() { diff --git a/syntax/impls.rs b/syntax/impls.rs index 7b2d746..acc99ab 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -86,15 +86,17 @@ impl PartialEq for Ref { fn eq(&self, other: &Ref) -> bool { let Ref { ampersand: _, + lifetime, mutability, inner, } = self; let Ref { ampersand: _, + lifetime: lifetime2, mutability: mutability2, inner: inner2, } = other; - mutability.is_some() == mutability2.is_some() && inner == inner2 + lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && inner == inner2 } } @@ -102,9 +104,11 @@ impl Hash for Ref { fn hash(&self, state: &mut H) { let Ref { ampersand: _, + lifetime, mutability, inner, } = self; + lifetime.hash(state); mutability.is_some().hash(state); inner.hash(state); } @@ -186,17 +190,21 @@ impl PartialEq for Receiver { fn eq(&self, other: &Receiver) -> bool { let Receiver { ampersand: _, + lifetime, mutability, var: _, ty, + shorthand: _, } = self; let Receiver { ampersand: _, + lifetime: lifetime2, mutability: mutability2, var: _, ty: ty2, + shorthand: _, } = other; - mutability.is_some() == mutability2.is_some() && ty == ty2 + lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && ty == ty2 } } @@ -204,10 +212,13 @@ impl Hash for Receiver { fn hash(&self, state: &mut H) { let Receiver { ampersand: _, + lifetime, mutability, var: _, ty, + shorthand: _, } = self; + lifetime.hash(state); mutability.is_some().hash(state); ty.hash(state); } diff --git a/syntax/mod.rs b/syntax/mod.rs index 3a2bb93..ab5cecc 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -19,7 +19,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{LitStr, Token}; +use syn::{Lifetime, LitStr, Token}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -76,9 +76,11 @@ pub struct Var { pub struct Receiver { pub ampersand: Token![&], + pub lifetime: Option, pub mutability: Option, pub var: Token![self], pub ty: Ident, + pub shorthand: bool, } pub enum Type { @@ -102,6 +104,7 @@ pub struct Ty1 { pub struct Ref { pub ampersand: Token![&], + pub lifetime: Option, pub mutability: Option, pub inner: Type, } diff --git a/syntax/parse.rs b/syntax/parse.rs index 8cc4e2a..bdccbda 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -177,12 +177,14 @@ fn parse_extern_fn( match arg { FnArg::Receiver(arg) => { if let Some(ety) = single_type { - if let Some((ampersand, _)) = arg.reference { + if let Some((ampersand, lifetime)) = &arg.reference { receiver = Some(Receiver { - ampersand, + ampersand: *ampersand, + lifetime: lifetime.clone(), mutability: arg.mutability, - var: Token![self](ety.ident.span()), + var: arg.self_token, ty: ety.ident.clone(), + shorthand: true, }); continue; } @@ -206,9 +208,11 @@ fn parse_extern_fn( if let Type::Ident(ident) = reference.inner { receiver = Some(Receiver { ampersand: reference.ampersand, + lifetime: reference.lifetime, mutability: reference.mutability, var: Token![self](ident.span()), ty: ident, + shorthand: false, }); continue; } @@ -273,6 +277,7 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }; Ok(which(Box::new(Ref { ampersand: ty.and_token, + lifetime: ty.lifetime.clone(), mutability: ty.mutability, inner, }))) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 08c966a..26bb3d1 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -47,6 +47,7 @@ impl ToTokens for Ty1 { impl ToTokens for Ref { fn to_tokens(&self, tokens: &mut TokenStream) { self.ampersand.to_tokens(tokens); + self.lifetime.to_tokens(tokens); self.mutability.to_tokens(tokens); self.inner.to_tokens(tokens); } @@ -110,6 +111,7 @@ impl Receiver { impl ToTokens for ReceiverType<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { self.0.ampersand.to_tokens(tokens); + self.0.lifetime.to_tokens(tokens); self.0.mutability.to_tokens(tokens); self.0.ty.to_tokens(tokens); } diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs new file mode 100644 index 0000000..ee39fef --- /dev/null +++ b/tests/ui/disallow_lifetime.rs @@ -0,0 +1,13 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type C; + fn f(&'static self); + } + + extern "Rust" { + fn f(string: &'a String); + } +} + +fn main() {} diff --git a/tests/ui/disallow_lifetime.stderr b/tests/ui/disallow_lifetime.stderr new file mode 100644 index 0000000..d7c42e4 --- /dev/null +++ b/tests/ui/disallow_lifetime.stderr @@ -0,0 +1,11 @@ +error: references with explicit lifetimes are not supported + --> $DIR/disallow_lifetime.rs:9:22 + | +9 | fn f(string: &'a String); + | ^^^^^^^^^^ + +error: references with explicit lifetimes are not supported + --> $DIR/disallow_lifetime.rs:5:14 + | +5 | fn f(&'static self); + | ^^^^^^^^^^^^^ From 8b60bf170bc4a96804d3c7817c3903191dc3ccdd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:54:15 +0000 Subject: [PATCH 328/2232] Reject unrecognized idents in receiver's type --- diff --git a/syntax/check.rs b/syntax/check.rs index fa51ed3..f6e46fd 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -141,6 +141,14 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { + if !cx.types.structs.contains_key(&receiver.ty) + && !cx.types.cxx.contains(&receiver.ty) + && !cx.types.rust.contains(&receiver.ty) + { + let span = span_for_receiver_error(receiver); + cx.error(span, "unrecognized receiver type"); + } + if receiver.lifetime.is_some() { let span = span_for_receiver_error(receiver); cx.error(span, "references with explicit lifetimes are not supported"); From 7a03847db2760b59104b02e798739b224a44f019 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 22 2020 23:54:34 +0000 Subject: [PATCH 329/2232] Add ui test for unrecognized receiver type --- diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs new file mode 100644 index 0000000..823bfcf --- /dev/null +++ b/tests/ui/unrecognized_receiver.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + fn f(self: &Unrecognized); + } +} + +fn main() {} diff --git a/tests/ui/unrecognized_receiver.stderr b/tests/ui/unrecognized_receiver.stderr new file mode 100644 index 0000000..a44bec1 --- /dev/null +++ b/tests/ui/unrecognized_receiver.stderr @@ -0,0 +1,5 @@ +error: unrecognized receiver type + --> $DIR/unrecognized_receiver.rs:4:20 + | +4 | fn f(self: &Unrecognized); + | ^^^^^^^^^^^^^ From c5e570bedb77399d97f2c8c44a71fd84e7ef6b47 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 00:10:12 +0000 Subject: [PATCH 330/2232] Merge pull request #134 from dtolnay/receiver Trigger error on unrecognized ident in receiver's type --- diff --git a/syntax/check.rs b/syntax/check.rs index fa51ed3..f6e46fd 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -141,6 +141,14 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { + if !cx.types.structs.contains_key(&receiver.ty) + && !cx.types.cxx.contains(&receiver.ty) + && !cx.types.rust.contains(&receiver.ty) + { + let span = span_for_receiver_error(receiver); + cx.error(span, "unrecognized receiver type"); + } + if receiver.lifetime.is_some() { let span = span_for_receiver_error(receiver); cx.error(span, "references with explicit lifetimes are not supported"); diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs new file mode 100644 index 0000000..823bfcf --- /dev/null +++ b/tests/ui/unrecognized_receiver.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + fn f(self: &Unrecognized); + } +} + +fn main() {} diff --git a/tests/ui/unrecognized_receiver.stderr b/tests/ui/unrecognized_receiver.stderr new file mode 100644 index 0000000..a44bec1 --- /dev/null +++ b/tests/ui/unrecognized_receiver.stderr @@ -0,0 +1,5 @@ +error: unrecognized receiver type + --> $DIR/unrecognized_receiver.rs:4:20 + | +4 | fn f(self: &Unrecognized); + | ^^^^^^^^^^^^^ From 0b368ae3412aceddb970da978cd4771f6b818a54 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 00:55:09 +0000 Subject: [PATCH 331/2232] Defer error on restricted type names --- diff --git a/syntax/check.rs b/syntax/check.rs index f6e46fd..0d25e77 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{ - error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, + error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; @@ -39,6 +39,7 @@ fn do_typecheck(cx: &mut Check) { for api in cx.apis { match api { Api::Struct(strct) => check_api_struct(cx, strct), + Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} } @@ -119,6 +120,8 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { } fn check_api_struct(cx: &mut Check, strct: &Struct) { + check_reserved_name(cx, &strct.ident); + if strct.fields.is_empty() { let span = span_for_struct_error(strct); cx.error(span, "structs without any fields are not supported"); @@ -139,6 +142,10 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } } +fn check_api_type(cx: &mut Check, ty: &ExternType) { + check_reserved_name(cx, &ty.ident); +} + fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { if !cx.types.structs.contains_key(&receiver.ty) @@ -228,6 +235,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } +fn check_reserved_name(cx: &mut Check, ident: &Ident) { + if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { + cx.error(ident, "reserved name"); + } +} + fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, diff --git a/syntax/parse.rs b/syntax/parse.rs index bdccbda..060f133 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,9 +1,8 @@ use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, Var, + attrs, error, Api, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, + Ty1, Type, Var, }; -use proc_macro2::Ident; use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ @@ -51,7 +50,6 @@ fn parse_struct(item: ItemStruct) -> Result { let mut doc = Doc::new(); let mut derives = Vec::new(); attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?; - check_reserved_name(&item.ident)?; let fields = match item.fields { Fields::Named(fields) => fields, @@ -94,7 +92,6 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let mut types = Vec::new(); for foreign in &foreign_mod.items { if let ForeignItem::Type(foreign) = foreign { - check_reserved_name(&foreign.ident)?; let ety = parse_extern_type(foreign)?; types.push(ety); } @@ -396,11 +393,3 @@ fn parse_return_type( ty => Ok(Some(ty)), } } - -fn check_reserved_name(ident: &Ident) -> Result<()> { - if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { - Err(Error::new(ident.span(), "reserved name")) - } else { - Ok(()) - } -} From 2dd73eaf03e14894b3dc003ee90e36eb13e24c50 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 00:56:27 +0000 Subject: [PATCH 332/2232] Add ui test for reserved type names --- diff --git a/tests/ui/reserved_name.rs b/tests/ui/reserved_name.rs new file mode 100644 index 0000000..27acca5 --- /dev/null +++ b/tests/ui/reserved_name.rs @@ -0,0 +1,16 @@ +#[cxx::bridge] +mod ffi { + struct UniquePtr { + val: usize, + } + + extern "C" { + type Box; + } + + extern "Rust" { + type String; + } +} + +fn main() {} diff --git a/tests/ui/reserved_name.stderr b/tests/ui/reserved_name.stderr new file mode 100644 index 0000000..6684771 --- /dev/null +++ b/tests/ui/reserved_name.stderr @@ -0,0 +1,17 @@ +error: reserved name + --> $DIR/reserved_name.rs:3:12 + | +3 | struct UniquePtr { + | ^^^^^^^^^ + +error: reserved name + --> $DIR/reserved_name.rs:8:14 + | +8 | type Box; + | ^^^ + +error: reserved name + --> $DIR/reserved_name.rs:12:14 + | +12 | type String; + | ^^^^^^ From 0b956da64a33c19eaa778194b49c61e062722010 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:05:11 +0000 Subject: [PATCH 333/2232] Merge pull request #135 from dtolnay/reserved Defer error on restricted type names --- diff --git a/syntax/check.rs b/syntax/check.rs index f6e46fd..0d25e77 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{ - error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, + error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; @@ -39,6 +39,7 @@ fn do_typecheck(cx: &mut Check) { for api in cx.apis { match api { Api::Struct(strct) => check_api_struct(cx, strct), + Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} } @@ -119,6 +120,8 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { } fn check_api_struct(cx: &mut Check, strct: &Struct) { + check_reserved_name(cx, &strct.ident); + if strct.fields.is_empty() { let span = span_for_struct_error(strct); cx.error(span, "structs without any fields are not supported"); @@ -139,6 +142,10 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } } +fn check_api_type(cx: &mut Check, ty: &ExternType) { + check_reserved_name(cx, &ty.ident); +} + fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { if !cx.types.structs.contains_key(&receiver.ty) @@ -228,6 +235,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } +fn check_reserved_name(cx: &mut Check, ident: &Ident) { + if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { + cx.error(ident, "reserved name"); + } +} + fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, diff --git a/syntax/parse.rs b/syntax/parse.rs index bdccbda..060f133 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,9 +1,8 @@ use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Atom, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, Var, + attrs, error, Api, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, + Ty1, Type, Var, }; -use proc_macro2::Ident; use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ @@ -51,7 +50,6 @@ fn parse_struct(item: ItemStruct) -> Result { let mut doc = Doc::new(); let mut derives = Vec::new(); attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?; - check_reserved_name(&item.ident)?; let fields = match item.fields { Fields::Named(fields) => fields, @@ -94,7 +92,6 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let mut types = Vec::new(); for foreign in &foreign_mod.items { if let ForeignItem::Type(foreign) = foreign { - check_reserved_name(&foreign.ident)?; let ety = parse_extern_type(foreign)?; types.push(ety); } @@ -396,11 +393,3 @@ fn parse_return_type( ty => Ok(Some(ty)), } } - -fn check_reserved_name(ident: &Ident) -> Result<()> { - if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { - Err(Error::new(ident.span(), "reserved name")) - } else { - Ok(()) - } -} diff --git a/tests/ui/reserved_name.rs b/tests/ui/reserved_name.rs new file mode 100644 index 0000000..27acca5 --- /dev/null +++ b/tests/ui/reserved_name.rs @@ -0,0 +1,16 @@ +#[cxx::bridge] +mod ffi { + struct UniquePtr { + val: usize, + } + + extern "C" { + type Box; + } + + extern "Rust" { + type String; + } +} + +fn main() {} diff --git a/tests/ui/reserved_name.stderr b/tests/ui/reserved_name.stderr new file mode 100644 index 0000000..6684771 --- /dev/null +++ b/tests/ui/reserved_name.stderr @@ -0,0 +1,17 @@ +error: reserved name + --> $DIR/reserved_name.rs:3:12 + | +3 | struct UniquePtr { + | ^^^^^^^^^ + +error: reserved name + --> $DIR/reserved_name.rs:8:14 + | +8 | type Box; + | ^^^ + +error: reserved name + --> $DIR/reserved_name.rs:12:14 + | +12 | type String; + | ^^^^^^ From 9bfbea37c742dead8901f955c24ccc9c14ac7954 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:12:49 +0000 Subject: [PATCH 334/2232] Allow mutating signature through ExternFn --- diff --git a/syntax/impls.rs b/syntax/impls.rs index acc99ab..a2ce05b 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,7 +1,7 @@ use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::hash::{Hash, Hasher}; use std::mem; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; impl Deref for ExternFn { type Target = Signature; @@ -11,6 +11,12 @@ impl Deref for ExternFn { } } +impl DerefMut for ExternFn { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.sig + } +} + impl Hash for Type { fn hash(&self, state: &mut H) { mem::discriminant(self).hash(state); From a1f29c4e430c9bae1e316ef41b78f010b2b1141a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:20:58 +0000 Subject: [PATCH 335/2232] Defer computing the Self type of methods --- diff --git a/syntax/check.rs b/syntax/check.rs index 0d25e77..5e8cc09 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -148,16 +148,28 @@ fn check_api_type(cx: &mut Check, ty: &ExternType) { fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { - if !cx.types.structs.contains_key(&receiver.ty) + let ref span = span_for_receiver_error(receiver); + + if receiver.ty == "Self" { + let mutability = match receiver.mutability { + Some(_) => "mut ", + None => "", + }; + 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.structs.contains_key(&receiver.ty) && !cx.types.cxx.contains(&receiver.ty) && !cx.types.rust.contains(&receiver.ty) { - let span = span_for_receiver_error(receiver); cx.error(span, "unrecognized receiver type"); } if receiver.lifetime.is_some() { - let span = span_for_receiver_error(receiver); cx.error(span, "references with explicit lifetimes are not supported"); } } diff --git a/syntax/parse.rs b/syntax/parse.rs index 060f133..7d92b1b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -89,24 +89,15 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { Lang::Rust => Api::RustFunction, }; - let mut types = Vec::new(); - for foreign in &foreign_mod.items { - if let ForeignItem::Type(foreign) = foreign { - let ety = parse_extern_type(foreign)?; - types.push(ety); - } - } - let single_type = if types.len() == 1 { - Some(&types[0]) - } else { - None - }; let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(_) => {} + ForeignItem::Type(foreign) => { + let ety = parse_extern_type(foreign)?; + items.push(api_type(ety)); + } ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang, &single_type)?; + let efn = parse_extern_fn(foreign, lang)?; items.push(api_function(efn)); } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -116,7 +107,24 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), } } - items.extend(types.into_iter().map(|ety| api_type(ety))); + + let mut types = items.iter().filter_map(|item| match item { + Api::CxxType(ty) | Api::RustType(ty) => Some(ty), + _ => None, + }); + if let (Some(single_type), None) = (types.next(), types.next()) { + let single_type = single_type.ident.clone(); + for item in &mut items { + if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { + if let Some(receiver) = &mut efn.receiver { + if receiver.ty == "Self" { + receiver.ty = single_type.clone(); + } + } + } + } + } + Ok(items) } @@ -148,11 +156,7 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { }) } -fn parse_extern_fn( - foreign_fn: &ForeignItemFn, - lang: Lang, - single_type: &Option<&ExternType>, -) -> Result { +fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -173,18 +177,16 @@ fn parse_extern_fn( let (arg, comma) = arg.into_tuple(); match arg { FnArg::Receiver(arg) => { - if let Some(ety) = single_type { - if let Some((ampersand, lifetime)) = &arg.reference { - receiver = Some(Receiver { - ampersand: *ampersand, - lifetime: lifetime.clone(), - mutability: arg.mutability, - var: arg.self_token, - ty: ety.ident.clone(), - shorthand: true, - }); - continue; - } + if let Some((ampersand, lifetime)) = &arg.reference { + receiver = Some(Receiver { + ampersand: *ampersand, + lifetime: lifetime.clone(), + mutability: arg.mutability, + var: arg.self_token, + ty: Token![Self](arg.self_token.span).into(), + shorthand: true, + }); + continue; } return Err(Error::new_spanned(arg, "unsupported signature")); } From ab73957a4e7416f744da1355af274d81a8989602 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:25:43 +0000 Subject: [PATCH 336/2232] Add ui test for unnamed receiver type error message --- diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs new file mode 100644 index 0000000..917e991 --- /dev/null +++ b/tests/ui/unnamed_receiver.rs @@ -0,0 +1,14 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type One; + type Two; + fn f(&mut self); + } + + extern "Rust" { + fn f(self: &Self); + } +} + +fn main() {} diff --git a/tests/ui/unnamed_receiver.stderr b/tests/ui/unnamed_receiver.stderr new file mode 100644 index 0000000..e66d9e3 --- /dev/null +++ b/tests/ui/unnamed_receiver.stderr @@ -0,0 +1,11 @@ +error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &mut TheType` + --> $DIR/unnamed_receiver.rs:6:14 + | +6 | fn f(&mut self); + | ^^^^^^^^^ + +error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &TheType` + --> $DIR/unnamed_receiver.rs:10:20 + | +10 | fn f(self: &Self); + | ^^^^^ From 50f8896bedecc5d9ff1d560dc7833a71b62b4dbe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:35:22 +0000 Subject: [PATCH 337/2232] Merge pull request #136 from dtolnay/receiver Defer computing the Self type of methods --- diff --git a/syntax/check.rs b/syntax/check.rs index 0d25e77..5e8cc09 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -148,16 +148,28 @@ fn check_api_type(cx: &mut Check, ty: &ExternType) { fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { - if !cx.types.structs.contains_key(&receiver.ty) + let ref span = span_for_receiver_error(receiver); + + if receiver.ty == "Self" { + let mutability = match receiver.mutability { + Some(_) => "mut ", + None => "", + }; + 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.structs.contains_key(&receiver.ty) && !cx.types.cxx.contains(&receiver.ty) && !cx.types.rust.contains(&receiver.ty) { - let span = span_for_receiver_error(receiver); cx.error(span, "unrecognized receiver type"); } if receiver.lifetime.is_some() { - let span = span_for_receiver_error(receiver); cx.error(span, "references with explicit lifetimes are not supported"); } } diff --git a/syntax/parse.rs b/syntax/parse.rs index 060f133..7d92b1b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -89,24 +89,15 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { Lang::Rust => Api::RustFunction, }; - let mut types = Vec::new(); - for foreign in &foreign_mod.items { - if let ForeignItem::Type(foreign) = foreign { - let ety = parse_extern_type(foreign)?; - types.push(ety); - } - } - let single_type = if types.len() == 1 { - Some(&types[0]) - } else { - None - }; let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(_) => {} + ForeignItem::Type(foreign) => { + let ety = parse_extern_type(foreign)?; + items.push(api_type(ety)); + } ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang, &single_type)?; + let efn = parse_extern_fn(foreign, lang)?; items.push(api_function(efn)); } ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -116,7 +107,24 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), } } - items.extend(types.into_iter().map(|ety| api_type(ety))); + + let mut types = items.iter().filter_map(|item| match item { + Api::CxxType(ty) | Api::RustType(ty) => Some(ty), + _ => None, + }); + if let (Some(single_type), None) = (types.next(), types.next()) { + let single_type = single_type.ident.clone(); + for item in &mut items { + if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { + if let Some(receiver) = &mut efn.receiver { + if receiver.ty == "Self" { + receiver.ty = single_type.clone(); + } + } + } + } + } + Ok(items) } @@ -148,11 +156,7 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { }) } -fn parse_extern_fn( - foreign_fn: &ForeignItemFn, - lang: Lang, - single_type: &Option<&ExternType>, -) -> Result { +fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -173,18 +177,16 @@ fn parse_extern_fn( let (arg, comma) = arg.into_tuple(); match arg { FnArg::Receiver(arg) => { - if let Some(ety) = single_type { - if let Some((ampersand, lifetime)) = &arg.reference { - receiver = Some(Receiver { - ampersand: *ampersand, - lifetime: lifetime.clone(), - mutability: arg.mutability, - var: arg.self_token, - ty: ety.ident.clone(), - shorthand: true, - }); - continue; - } + if let Some((ampersand, lifetime)) = &arg.reference { + receiver = Some(Receiver { + ampersand: *ampersand, + lifetime: lifetime.clone(), + mutability: arg.mutability, + var: arg.self_token, + ty: Token![Self](arg.self_token.span).into(), + shorthand: true, + }); + continue; } return Err(Error::new_spanned(arg, "unsupported signature")); } diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs new file mode 100644 index 0000000..917e991 --- /dev/null +++ b/tests/ui/unnamed_receiver.rs @@ -0,0 +1,14 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type One; + type Two; + fn f(&mut self); + } + + extern "Rust" { + fn f(self: &Self); + } +} + +fn main() {} diff --git a/tests/ui/unnamed_receiver.stderr b/tests/ui/unnamed_receiver.stderr new file mode 100644 index 0000000..e66d9e3 --- /dev/null +++ b/tests/ui/unnamed_receiver.stderr @@ -0,0 +1,11 @@ +error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &mut TheType` + --> $DIR/unnamed_receiver.rs:6:14 + | +6 | fn f(&mut self); + | ^^^^^^^^^ + +error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &TheType` + --> $DIR/unnamed_receiver.rs:10:20 + | +10 | fn f(self: &Self); + | ^^^^^ From 274da11cc3ccc540b193ceda7b45ca18f0f93c25 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:38:00 +0000 Subject: [PATCH 338/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index af14713..9696c97 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -14,7 +14,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.50/src/**"]), + srcs = glob(["vendor/cc-1.0.52/src/**"]), visibility = ["PUBLIC"], ) @@ -107,7 +107,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.13/src/**"]), + srcs = glob(["vendor/structopt-0.3.14/src/**"]), visibility = ["PUBLIC"], deps = [ ":clap", @@ -118,7 +118,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.6/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.7/src/**"]), proc_macro = True, deps = [ ":heck", diff --git a/third-party/BUILD b/third-party/BUILD index 25a3dd6..a3db8b1 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -19,7 +19,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.50/src/**"]), + srcs = glob(["vendor/cc-1.0.52/src/**"]), visibility = ["//visibility:public"], ) @@ -112,7 +112,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.13/src/**"]), + srcs = glob(["vendor/structopt-0.3.14/src/**"]), visibility = ["//visibility:public"], deps = [ ":clap", @@ -123,7 +123,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.6/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.7/src/**"]), crate_type = "proc-macro", deps = [ ":heck", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 71ad826..1aba3c9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -34,9 +34,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" [[package]] name = "clap" @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" dependencies = [ "libc", ] @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "serde" @@ -269,9 +269,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6da2e8d107dfd7b74df5ef4d205c6aebee0706c647f6bc6a2d5789905c00fb" +checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" dependencies = [ "clap", "lazy_static", @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a489c87c08fbaf12e386665109dd13470dcc9c4583ea3e10dd2b4523e5ebd9ac" +checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" dependencies = [ "heck", "proc-macro-error", @@ -422,9 +422,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] From 0d645c7d4020229a04442bb9b389318386470c10 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 01:38:34 +0000 Subject: [PATCH 339/2232] Release 0.2.10 --- diff --git a/Cargo.toml b/Cargo.toml index f9d9b52..e61fd2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.9" # remember to update html_root_url +version = "0.2.10" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.9", path = "macro" } +cxxbridge-macro = { version = "=0.2.10", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 7c0739b..6ec328e 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.9" +version = "0.2.10" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 386c9dd..ca0e326 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.9" +version = "0.2.10" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 609db59..74e103a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.9")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.10")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1aba3c9..7158ff0 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.9" +version = "0.2.10" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.9" +version = "0.2.10" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.9" +version = "0.2.10" dependencies = [ "cxx", "proc-macro2", From 3deb2f9d2a4b23681d76d2a254903cc54aea5d7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 02:16:38 +0000 Subject: [PATCH 340/2232] Remove checkboxes from readme in favor of filed issues --- diff --git a/README.md b/README.md index 508d43d..a8f4093 100644 --- a/README.md +++ b/README.md @@ -332,11 +332,8 @@ matter of designing a nice API for each in its non-native language. ## Remaining work This is still early days for CXX; I am releasing it as a minimum viable product -to collect feedback on the direction and invite collaborators. Here are some of -the facets that I still intend for this project to tackle: - -- [ ] Support structs with type parameters -- [ ] Support async functions +to collect feedback on the direction and invite collaborators. Please check the +open issues. On the build side, I don't have much experience with the `cc` crate so I expect there may be someone who can suggest ways to make that aspect of this crate From a3a0a6dfc57e8019c502b21dbd7aa4d2c94ed343 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 02:49:22 +0000 Subject: [PATCH 341/2232] Enable trybuild diffs --- diff --git a/Cargo.toml b/Cargo.toml index e61fd2f..f497482 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ cc = "1.0.49" [dev-dependencies] cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = "1.0.21" +trybuild = { version = "1.0.21", features = ["diff"] } [workspace] members = ["cmd", "demo-rs", "macro", "tests/ffi"] From 4b07ab92e3fc900a4d712691533e6b77188e9785 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 02:50:21 +0000 Subject: [PATCH 342/2232] Improve span of unsized opaque type error --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 36f25c5..8b3602a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -32,7 +32,8 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { has_rust_type = true; } let ident = &ety.ident; - hidden.extend(quote!(__assert_sized::<#ident>();)); + let span = ident.span(); + hidden.extend(quote_spanned!(span=> __assert_sized::<#ident>();)); } } diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 1146b80..e93a546 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -1,13 +1,12 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/opaque_not_sized.rs:1:1 + --> $DIR/opaque_not_sized.rs:4:14 | 1 | #[cxx::bridge] - | ^^^^^^^^^^^^^^ - | | - | doesn't have a size known at compile-time - | required by this bound in `ffi::_::__assert_sized` + | -------------- required by this bound in `ffi::_::__assert_sized` +... +4 | type TypeR; + | ^^^^^ doesn't have a size known at compile-time | = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` = note: to learn more, visit = note: required because it appears within the type `TypeR` - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From b17b9f3bf4b34a81e2a0f9226ef89bc0d3c4167a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 03:01:08 +0000 Subject: [PATCH 343/2232] Regenerate lockfile to include trybuild/diff feature --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 7158ff0..2e70499 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -119,6 +119,12 @@ dependencies = [ ] [[package]] +name = "dissimilar" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39de161cd2ebbd6e5783db53a82a47b6a47dcfef754130839603561745528b94" + +[[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -366,6 +372,7 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459186ab1afd6d93bd23c2269125f4f7694f8771fe0e64434b4bdc212b94034d" dependencies = [ + "dissimilar", "glob", "lazy_static", "serde", From 1044d44f308e97ab017ff05c3384e10b41e7913a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 03:01:21 +0000 Subject: [PATCH 344/2232] Suppress irrelevant "required by this bound" from error message --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8b3602a..3189bff 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -20,20 +20,15 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let mut has_rust_type = false; for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); - if !has_rust_type { - hidden.extend(quote!( - const fn __assert_sized() {} - )); - has_rust_type = true; - } let ident = &ety.ident; let span = ident.span(); - hidden.extend(quote_spanned!(span=> __assert_sized::<#ident>();)); + hidden.extend(quote_spanned! {span=> + let _ = ::std::ptr::read::<#ident>; + }); } } diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index e93a546..0baa70a 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -1,12 +1,9 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/opaque_not_sized.rs:4:14 - | -1 | #[cxx::bridge] - | -------------- required by this bound in `ffi::_::__assert_sized` -... -4 | type TypeR; - | ^^^^^ doesn't have a size known at compile-time - | - = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` - = note: to learn more, visit - = note: required because it appears within the type `TypeR` + --> $DIR/opaque_not_sized.rs:4:14 + | +4 | type TypeR; + | ^^^^^ doesn't have a size known at compile-time + | + = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: required because it appears within the type `TypeR` From 97408eed57cd17f92d5498587f2b65c63c3eebed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 03:27:29 +0000 Subject: [PATCH 345/2232] Merge pull request #141 from dtolnay/sized Improve span of unsized opaque type error --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8b3602a..3189bff 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -20,20 +20,15 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let mut has_rust_type = false; for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); - if !has_rust_type { - hidden.extend(quote!( - const fn __assert_sized() {} - )); - has_rust_type = true; - } let ident = &ety.ident; let span = ident.span(); - hidden.extend(quote_spanned!(span=> __assert_sized::<#ident>();)); + hidden.extend(quote_spanned! {span=> + let _ = ::std::ptr::read::<#ident>; + }); } } diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index e93a546..0baa70a 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -1,12 +1,9 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/opaque_not_sized.rs:4:14 - | -1 | #[cxx::bridge] - | -------------- required by this bound in `ffi::_::__assert_sized` -... -4 | type TypeR; - | ^^^^^ doesn't have a size known at compile-time - | - = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` - = note: to learn more, visit - = note: required because it appears within the type `TypeR` + --> $DIR/opaque_not_sized.rs:4:14 + | +4 | type TypeR; + | ^^^^^ doesn't have a size known at compile-time + | + = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: required because it appears within the type `TypeR` From 00377239222ead743ccb4a7b7ce75a840e89a964 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 23 2020 16:42:46 +0000 Subject: [PATCH 346/2232] Add CI of test suite including method syntax on 1.43 --- diff --git a/.travis.yml b/.travis.yml index 49bacbd..124fc57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: rust rust: - nightly - beta + - stable script: - cargo run --manifest-path demo-rs/Cargo.toml From cb4ee4bb71beb8a1f102dad701f719df2b87c4fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 18:28:34 +0000 Subject: [PATCH 347/2232] Fix missing absolute path to MaybeUninit It's possible for `std` to mean something different from `::std` if the user's ffi mod contains a type named `std`. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 3189bff..8223572 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -228,7 +228,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types // These are arguments for which C++ has taken ownership of the data // behind the mut reference it received. quote! { - let mut #var = std::mem::MaybeUninit::new(#var); + let mut #var = ::std::mem::MaybeUninit::new(#var); } }) .collect::(); From ad7186a89fa4f6057167a650e0e284f5d5703164 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 22:37:49 +0000 Subject: [PATCH 348/2232] Produce better error on const_assert failures The new message includes the value of the left and right side. The previous message did not include the values, just said it failed. --- diff --git a/src/assert.rs b/src/assert.rs index 6159ce6..cc736dc 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -1,30 +1,5 @@ -pub struct True; -pub struct False; - -pub trait ToBool { - type Bool: Sized; - const BOOL: Self::Bool; -} - -impl ToBool for [(); 0] { - type Bool = False; - const BOOL: Self::Bool = False; -} - -impl ToBool for [(); 1] { - type Bool = True; - const BOOL: Self::Bool = True; -} - -macro_rules! bool { - ($e:expr) => {{ - const EXPR: bool = $e; - <[(); EXPR as usize] as $crate::assert::ToBool>::BOOL - }}; -} - -macro_rules! const_assert { - ($e:expr) => { - const _: $crate::assert::True = bool!($e); +macro_rules! const_assert_eq { + ($left:expr, $right:expr) => { + const _: [(); $left] = [(); $right]; }; } diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index a0c348b..6e987a8 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -23,4 +23,4 @@ impl RustSliceU8 { } } -const_assert!(mem::size_of::>() == mem::size_of::()); +const_assert_eq!(mem::size_of::>(), mem::size_of::()); diff --git a/src/rust_str.rs b/src/rust_str.rs index 51e4835..184e2c1 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -31,4 +31,4 @@ unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { str::from_utf8(slice).is_ok() } -const_assert!(mem::size_of::>() == mem::size_of::()); +const_assert_eq!(mem::size_of::>(), mem::size_of::()); From aa77e82ec8b45ee86a410555cfb91864c210882b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 22:43:05 +0000 Subject: [PATCH 349/2232] Use const_assert_eq for all const assertions --- diff --git a/src/opaque.rs b/src/opaque.rs index ded64e9..0ff6bb9 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -10,7 +10,5 @@ pub struct Opaque { _private: [*const u8; 0], } -fn _assert() { - let _: [(); 0] = [(); mem::size_of::()]; - let _: [(); 1] = [(); mem::align_of::()]; -} +const_assert_eq!(0, mem::size_of::()); +const_assert_eq!(1, mem::align_of::()); diff --git a/src/rust_string.rs b/src/rust_string.rs index 113adfd..3fba2f5 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -71,7 +71,5 @@ unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } -fn _assert() { - let _: [(); mem::size_of::<[usize; 3]>()] = [(); mem::size_of::()]; - let _: [(); mem::align_of::()] = [(); mem::align_of::()]; -} +const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::()); +const_assert_eq!(mem::align_of::(), mem::align_of::()); From 7a9b1301acfaaa556051230600f5ef86dd77e592 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 23:15:36 +0000 Subject: [PATCH 350/2232] Relative link to local types in builtin types doc These links work for locally rendered offline documentation. --- diff --git a/src/lib.rs b/src/lib.rs index 74e103a..304a87a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,9 +308,9 @@ //! //! //! -//! +//! //! -//! +//! //! //! //!
name in Rustname in C++
&[T]tbd
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Stringrust::String
&strrust::Str
&[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet
CxxStringstd::stringcannot be passed by value
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
From d7b8a6e34c91f8c2c09015f389ac8f7e20909e75 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 23:22:55 +0000 Subject: [PATCH 351/2232] Link to std::string::empty --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index d3d128c..baad468 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -36,6 +36,10 @@ impl CxxString { } /// Returns true if `self` has a length of zero bytes. + /// + /// Matches the behavior of C++ [std::string::empty][empty]. + /// + /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty pub fn is_empty(&self) -> bool { self.len() == 0 } From 3cd990fe644ba16c4e6212a4749aa770491b4c0c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 24 2020 23:24:26 +0000 Subject: [PATCH 352/2232] Fix link from CxxString::as_ptr to len method --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index baad468..54fad27 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -58,9 +58,10 @@ impl CxxString { /// Note that the return type may look like `const char *` but is not a /// `const char *` in the typical C sense, as C++ strings may contain /// internal null bytes. As such, the returned pointer only makes sense as a - /// string in combination with the length returned by [`len()`](#len). + /// string in combination with the length returned by [`len()`][len]. /// /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data + /// [len]: #method.len pub fn as_ptr(&self) -> *const u8 { unsafe { string_data(self) } } From 9706a5104c8d01589813adbc6267c60d5217e3ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 25 2020 00:09:01 +0000 Subject: [PATCH 353/2232] Express Box const_pointer and pointer more concisely --- diff --git a/include/cxx.h b/include/cxx.h index 637372a..7c2594c 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -125,9 +125,9 @@ template class Box final { public: using value_type = T; - using const_pointer = typename std::add_pointer< - typename std::add_const::type>::type; - using pointer = typename std::add_pointer::type; + using const_pointer = + typename std::add_pointer::type>::type; + using pointer = typename std::add_pointer::type; Box(const Box &other) : Box(*other) {} Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } From e9f58d5cf3e3b8f4bd507c9a0b1b07db2c6fffd0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 25 2020 00:44:58 +0000 Subject: [PATCH 354/2232] Wrap long const assert --- diff --git a/src/assert.rs b/src/assert.rs index cc736dc..738e5bb 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -1,5 +1,5 @@ macro_rules! const_assert_eq { - ($left:expr, $right:expr) => { + ($left:expr, $right:expr $(,)?) => { const _: [(); $left] = [(); $right]; }; } diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index 6e987a8..f509c7f 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -23,4 +23,7 @@ impl RustSliceU8 { } } -const_assert_eq!(mem::size_of::>(), mem::size_of::()); +const_assert_eq!( + mem::size_of::>(), + mem::size_of::(), +); From eba35cfce75094c0e4b459f63cc1e8ab915c10af Mon Sep 17 00:00:00 2001 From: Myron Ahn Date: Apr 25 2020 19:47:04 +0000 Subject: [PATCH 355/2232] C++ std::vector and Rust std::vec::Vec support Add basic std::vector and std::vec::Vec support across FFI boundary. --- diff --git a/Cargo.toml b/Cargo.toml index f497482..95cd87d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" cc = "1.0.49" +codespan = "0.7" codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.10", path = "macro" } link-cplusplus = "1.0" diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..387c56a 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,13 +9,41 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(rust::Str appname) { +std::unique_ptr make_demo(::rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } const std::string &get_name(const ThingC &thing) { return thing.appname; } -void do_thing(SharedThing state) { print_r(*state.y); } +std::unique_ptr> do_thing(SharedThing state) { + print_r(*state.y); + auto vec = std::unique_ptr>(new std::vector()); + for (uint8_t i = 0; i < 10; i++) { + vec->push_back(i * i); + } + return vec; +} + +JsonBlob get_jb(const ::rust::Vec& vec) { + JsonBlob retval; + + std::cout << "incoming vec length is " << vec.size() << "\n"; + auto vec_copy = static_cast>(vec); + std::cout << "vec_copy length is " << vec_copy.size() << "\n"; + std::cout << "vec_copy[0] is " << (int)vec_copy[0] << "\n"; + + auto blob = std::unique_ptr>(new std::vector()); + for (uint8_t i = 0; i < 10; i++) { + blob->push_back(i * 2); + } + + auto json = std::unique_ptr(new std::string("{\"demo\": 23}")); + + retval.json = std::move(json); + retval.blob = std::move(blob); + + return retval; +} } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index fafc474..eea0af7 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -15,10 +15,12 @@ public: }; struct SharedThing; +struct JsonBlob; -std::unique_ptr make_demo(rust::Str appname); +std::unique_ptr make_demo(::rust::Str appname); const std::string &get_name(const ThingC &thing); -void do_thing(SharedThing state); +std::unique_ptr> do_thing(SharedThing state); +JsonBlob get_jb(const ::rust::Vec& vec); } // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 66dfc79..8bf9926 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -6,13 +6,19 @@ mod ffi { x: UniquePtr, } + struct JsonBlob { + json: UniquePtr, + blob: UniquePtr>, + } + extern "C" { include!("demo-cxx/demo.h"); type ThingC; fn make_demo(appname: &str) -> UniquePtr; fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing); + fn do_thing(state: SharedThing) -> UniquePtr>; + fn get_jb(v: &Vec) -> JsonBlob; } extern "Rust" { @@ -31,9 +37,24 @@ fn main() { let x = ffi::make_demo("demo of cxx::bridge"); println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - ffi::do_thing(ffi::SharedThing { + let vec = ffi::do_thing(ffi::SharedThing { z: 222, y: Box::new(ThingR(333)), x, }); + + println!("vec length = {}", vec.as_ref().unwrap().size()); + for (i, v) in vec.as_ref().unwrap().into_iter().enumerate() { + println!("vec[{}] = {}", i, v); + } + + let mut rv: Vec = Vec::new(); + for _ in 0..1000 { + rv.push(33); + } + let jb = ffi::get_jb(&rv); + println!("json: {}", jb.json.as_ref().unwrap()); + for (i, v) in jb.blob.as_ref().unwrap().into_iter().enumerate() { + println!("jb.blob[{}] = {}", i, v); + } } diff --git a/gen/include.rs b/gen/include.rs index 8f38fe3..077d03e 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -36,6 +36,7 @@ pub struct Includes { pub exception: bool, pub memory: bool, pub string: bool, + pub vector: bool, pub type_traits: bool, pub utility: bool, pub base_tsd: bool, diff --git a/gen/write.rs b/gen/write.rs index a8e3fd4..a74833f 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,8 +1,10 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; +use crate::syntax::typename::ToTypename; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -42,7 +44,7 @@ pub(super) fn gen( Api::Struct(strct) => write_struct_decl(out, &strct.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident), Api::RustType(ety) => write_struct_decl(out, &ety.ident), - _ => {} + _ => (), } } @@ -123,8 +125,9 @@ fn write_includes(out: &mut OutFile, types: &Types) { }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, + Type::Vector(_) => out.include.vector = true, Type::SliceRefU8(_) => out.include.cstdint = true, - _ => {} + _ => (), } } } @@ -134,14 +137,18 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_str = false; let mut needs_rust_slice = false; let mut needs_rust_box = false; + let mut needs_rust_vec = false; let mut needs_rust_fn = false; - let mut needs_rust_isize = false; for ty in types { match ty { Type::RustBox(_) => { out.include.type_traits = true; needs_rust_box = true; } + Type::RustVec(_) => { + out.include.type_traits = true; + needs_rust_vec = true; + } Type::Str(_) => { out.include.cstdint = true; out.include.string = true; @@ -153,10 +160,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Type::Slice(_) | Type::SliceRefU8(_) => { needs_rust_slice = true; } - ty if ty == Isize => { - out.include.base_tsd = true; - needs_rust_isize = true; - } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -213,9 +216,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_rust_str || needs_rust_slice || needs_rust_box + || needs_rust_vec || needs_rust_fn || needs_rust_error - || needs_rust_isize || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit @@ -233,9 +236,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); if needs_manually_drop { @@ -469,6 +472,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::Vector(_)) => write!( + out, + " /* Use RVO to convert to r-value and move construct */" + ), Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } @@ -779,7 +786,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -796,21 +803,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), + Some(a) => write!(out, "{}", a.to_cxx()), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { @@ -818,11 +811,21 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, ">"); } + Type::RustVec(ty) => { + write!(out, "::rust::Vec<"); + write_type(out, &ty.inner); + write!(out, ">"); + } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); write_type(out, &ptr.inner); write!(out, ">"); } + Type::Vector(ty) => { + write!(out, "::std::vector<"); + write_type(out, &ty.inner); + write!(out, ">"); + } Type::Ref(r) => { if r.mutability.is_none() { write!(out, "const "); @@ -870,6 +873,8 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) + | Type::Vector(_) + | Type::RustVec(_) | Type::SliceRefU8(_) | Type::Fn(_) => write!(out, " "), Type::Ref(_) => {} @@ -882,6 +887,11 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { Atom::from(ident).is_none() } + fn allow_vector(ident: &Ident) -> bool { + // Note: built-in types such as u8 are already defined in cxx.cc + Atom::from(ident).is_none() + } + out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -889,11 +899,30 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.next_section(); write_rust_box_extern(out, inner); } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(_) = &ty.inner { + out.next_section(); + write_rust_vec_extern(out, &ty.inner); + } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, inner, types); + write_unique_ptr(out, &ptr.inner, types); + } + } else if let Type::Vector(ptr1) = &ptr.inner { + if let Type::Ident(inner) = &ptr1.inner { + if allow_vector(inner) { + out.next_section(); + write_unique_ptr(out, &ptr.inner, types); + } + } + } + } else if let Type::Vector(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if allow_vector(inner) { + out.next_section(); + write_vector(out, inner); } } } @@ -907,6 +936,10 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ty.inner { write_rust_box_impl(out, inner); } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(_) = &ty.inner { + write_rust_vec_impl(out, &ty.inner); + } } } out.end_block("namespace cxxbridge02"); @@ -937,6 +970,31 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); } +fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!( + out, + "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const std::vector<{}> &vector) noexcept;", + instance, inner, inner + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); +} + fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { let mut inner = String::new(); for name in &out.namespace { @@ -957,16 +1015,44 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { - out.include.utility = true; +fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); + writeln!(out, "template <>"); + writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$drop(this);", + instance + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!( + out, + "Vec<{}>::operator std::vector<{}>() const noexcept {{", + inner, inner + ); + writeln!( + out, + " std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", + inner, instance, + ); + writeln!(out, "}}"); +} + +fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.utility = true; + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); @@ -987,18 +1073,21 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - if types.structs.contains_key(ident) { - writeln!( + match ty { + Type::Ident(ident) if types.structs.contains_key(ident) => { + writeln!( out, "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } + _ => (), } writeln!( out, @@ -1030,3 +1119,50 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { writeln!(out, "}}"); writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); } + +fn write_vector(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in &out.namespace { + inner += name; + inner += "::"; + } + } + let mut instance = inner.clone(); + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + instance += &ident.to_string(); + let instance = instance.replace("::", "$"); + + writeln!(out, "#ifndef CXXBRIDGE02_vector_{}", instance); + writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); + writeln!( + out, + "size_t cxxbridge02$std$vector${}$length(const std::vector<{}> &s) noexcept {{", + instance, inner, + ); + writeln!(out, " return s.size();"); + writeln!(out, "}}"); + + writeln!( + out, + "void cxxbridge02$std$vector${}$push_back(std::vector<{}> &s, const {} &item) noexcept {{", + instance, inner, inner + ); + writeln!(out, " s.push_back(item);"); + writeln!(out, "}}"); + + writeln!( + out, + "const {} *cxxbridge02$std$vector${}$get_unchecked(const std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return &s[pos];"); + writeln!(out, "}}"); + writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); +} diff --git a/include/cxx.h b/include/cxx.h index 637372a..35e9059 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #if defined(_WIN32) @@ -83,6 +84,27 @@ private: }; #endif // CXXBRIDGE02_RUST_STR +#ifndef CXXBRIDGE02_RUST_VEC +#define CXXBRIDGE02_RUST_VEC +template +class Vec final { +public: + size_t size() const noexcept; + explicit operator std::vector() const noexcept; + +private: + Vec() noexcept; + Vec(const Vec &other) noexcept; + Vec &operator=(Vec other) noexcept; + void drop() noexcept; + + // Repr + const T *ptr; + size_t len; + size_t capacity; +}; +#endif // CXXBRIDGE02_RUST_VEC + #ifndef CXXBRIDGE02_RUST_SLICE #define CXXBRIDGE02_RUST_SLICE template @@ -234,14 +256,11 @@ private: }; #endif // CXXBRIDGE02_RUST_ERROR -#ifndef CXXBRIDGE02_RUST_ISIZE -#define CXXBRIDGE02_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif -#endif // CXXBRIDGE02_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); diff --git a/macro/Cargo.toml b/macro/Cargo.toml index ca0e326..99ae923 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -19,7 +19,7 @@ quote = "1.0" syn = { version = "1.0", features = ["full"] } [dev-dependencies] -cxx = { version = "0.2", path = ".." } +cxx = { version = "0.2.7-alpha-1", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8b3602a..b329276 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,12 +1,14 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; +use crate::syntax::typename::ToTypename; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, Error, ItemMod, Result, Token}; +use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -22,6 +24,38 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut hidden = TokenStream::new(); let mut has_rust_type = false; + // "Header" to define newtypes locally so we can implement + // traits on them. + expanded.extend(quote! { + pub struct Vector(pub ::cxx::RealVector); + impl> Vector { + pub fn size(&self) -> usize { + self.0.size() + } + pub fn get(&self, pos: usize) -> Option<&T> { + self.0.get(pos) + } + pub fn get_unchecked(&self, pos: usize) -> &T { + self.0.get_unchecked(pos) + } + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + pub fn push_back(&mut self, item: &T) { + self.0.push_back(item) + } + } + impl<'a, T: cxx::private::VectorTarget> IntoIterator for &'a Vector { + type Item = &'a T; + type IntoIter = <&'a ::cxx::RealVector as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + unsafe impl Send for Vector where T: Send + cxx::private::VectorTarget {} + }); + for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); @@ -58,10 +92,34 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { hidden.extend(expand_rust_box(namespace, ident)); } } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(ident) = &ty.inner { + hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, ident, types)); + expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); + } + } else if let Type::Vector(_) = &ptr.inner { + // Generate code for unique_ptr> if T is not an atom + // or if T is a primitive. + // Code for primitives is already generated + match Atom::from(ident) { + None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)), + Some(atom) => { + if atom.is_valid_vector_target() { + expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); + } + } + } + } + } else if let Type::Vector(ptr) = ty { + if let Type::Ident(ident) = &ptr.inner { + if Atom::from(ident).is_none() { + // Generate code for Vector if T is not an atom + // Code for atoms is already generated + expanded.extend(expand_vector(namespace, &ptr.inner)); } } } @@ -197,10 +255,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), + Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) } + Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)), _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -523,9 +583,42 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); +fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); + let link_drop = format!("{}drop", link_prefix); + let link_vector_from = format!("{}vector_from", link_prefix); + let link_len = format!("{}len", link_prefix); + + let local_prefix = format_ident!("{}__vec_", ident); + let local_drop = format_ident!("{}drop", local_prefix); + let local_vector_from = format_ident!("{}vector_from", local_prefix); + let local_len = format_ident!("{}len", local_prefix); + + let span = ty.span(); + quote_spanned! {span=> + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { + std::ptr::drop_in_place(this); + } + #[export_name = #link_vector_from] + unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) { + this.as_ref().unwrap().into_vector(vector.as_mut().unwrap()); + } + #[export_name = #link_len] + unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { + this.as_ref().unwrap().len() + } + } +} + +fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { + let name = ty.to_typename(&namespace.segments); + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$unique_ptr${}", mangled); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -533,8 +626,8 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) { - Some(quote! { + let new_method = match ty { + Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] @@ -544,13 +637,12 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe { __new(&mut repr, &mut value) } repr } - }) - } else { - None + }), + _ => None, }; quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #ident { + unsafe impl ::cxx::private::UniquePtrTarget for #inner { const __NAME: &'static str = #name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { @@ -565,7 +657,7 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] - fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident); + fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner); } let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); __raw(&mut repr, raw); @@ -574,14 +666,14 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self { extern "C" { #[link_name = #link_get] - fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident; + fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner; } __get(&repr) } unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self { extern "C" { #[link_name = #link_release] - fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident; + fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner; } __release(&mut repr) } @@ -596,6 +688,90 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok } } +fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$std$vector${}", mangled); + let link_length = format!("{}length", prefix); + let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_push_back = format!("{}push_back", prefix); + + quote! { + impl ::cxx::private::VectorTarget<#inner> for #inner { + fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner; + } + unsafe { + __get_unchecked(v, pos) + } + } + fn vector_length(v: &::cxx::RealVector<#inner>) -> usize { + unsafe { + extern "C" { + #[link_name = #link_length] + fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize; + } + __vector_length(v) + } + } + fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) { + unsafe { + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize; + } + __push_back(v, item); + } + } + } + } +} + +pub fn expand_vector_builtin(ident: Ident) -> TokenStream { + let ty = Type::Ident(ident); + let inner = &ty; + let namespace = Namespace { segments: vec![] }; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$std$vector${}", mangled); + let link_length = format!("{}length", prefix); + let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_push_back = format!("{}push_back", prefix); + + quote! { + impl VectorTarget<#inner> for #inner { + fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner; + } + unsafe { + __get_unchecked(v, pos) + } + } + fn vector_length(v: &RealVector<#inner>) -> usize { + unsafe { + extern "C" { + #[link_name = #link_length] + fn __vector_length(_: &RealVector<#inner>) -> usize; + } + __vector_length(v) + } + } + fn push_back(v: &RealVector<#inner>, item: &#inner) { + unsafe { + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize; + } + __push_back(v, item); + } + } + } + } +} + fn expand_return_type(ret: &Option) -> TokenStream { match ret { Some(ret) => quote!(-> #ret), @@ -613,11 +789,16 @@ fn expand_extern_type(ty: &Type) -> TokenStream { match ty { Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString), Type::RustBox(ty) | Type::UniquePtr(ty) => { - let inner = &ty.inner; + let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) } + Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), + Type::RustVec(ty) => { + let inner = expand_extern_type(&ty.inner); + quote!(&::cxx::RustVec<#inner>) + } _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b56f58e..87fe405 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,7 +14,7 @@ mod syntax; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod}; +use syn::{parse_macro_input, Ident, ItemMod}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,3 +44,9 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +#[proc_macro] +pub fn vector_builtin(input: TokenStream) -> TokenStream { + let ident = parse_macro_input!(input as Ident); + expand::expand_vector_builtin(ident).into() +} diff --git a/src/cxx.cc b/src/cxx.cc index b64333c..916f525 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include template @@ -197,3 +198,50 @@ void cxxbridge02$unique_ptr$std$string$drop( ptr->~unique_ptr(); } } // extern "C" + +#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ +extern "C" { \ +size_t cxxbridge02$std$vector$##RUST_TYPE##$length(const std::vector &s) noexcept { \ + return s.size(); \ +} \ +void cxxbridge02$std$vector$##RUST_TYPE##$push_back(std::vector &s, const CXX_TYPE &item) noexcept { \ + s.push_back(item); \ +} \ +const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked(const std::vector &s, size_t pos) noexcept { \ + return &s[pos]; \ +} \ +static_assert(sizeof(::std::unique_ptr>) == sizeof(void *), ""); \ +static_assert(alignof(::std::unique_ptr>) == alignof(void *), ""); \ +void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null(::std::unique_ptr> *ptr) noexcept { \ + new (ptr) ::std::unique_ptr>(); \ +} \ +void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new(::std::unique_ptr> *ptr, std::vector *value) noexcept { \ + new (ptr) ::std::unique_ptr>(new std::vector(::std::move(*value))); \ +} \ +void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw(::std::unique_ptr> *ptr, std::vector *raw) noexcept { \ + new (ptr) ::std::unique_ptr>(raw); \ +} \ +const std::vector *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get(const ::std::unique_ptr>& ptr) noexcept { \ + return ptr.get(); \ +} \ +std::vector *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release(::std::unique_ptr>& ptr) noexcept { \ + return ptr.release(); \ +} \ +void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop(::std::unique_ptr> *ptr) noexcept { \ + ptr->~unique_ptr(); \ +} \ +} // extern "C" + +STD_VECTOR_OPS(u8, uint8_t); +STD_VECTOR_OPS(u16, uint16_t); +STD_VECTOR_OPS(u32, uint32_t); +STD_VECTOR_OPS(u64, uint64_t); +STD_VECTOR_OPS(usize, size_t); +STD_VECTOR_OPS(i8, int8_t); +STD_VECTOR_OPS(i16, int16_t); +STD_VECTOR_OPS(i32, int32_t); +STD_VECTOR_OPS(i64, int64_t); +STD_VECTOR_OPS(isize, rust::isize); +STD_VECTOR_OPS(f32, float); +STD_VECTOR_OPS(f64, double); + diff --git a/src/lib.rs b/src/lib.rs index 74e103a..994c430 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -370,13 +370,18 @@ mod result; mod rust_sliceu8; mod rust_str; mod rust_string; +mod rust_vec; mod syntax; mod unique_ptr; mod unwind; +mod vector; pub use crate::cxx_string::CxxString; pub use crate::exception::Exception; +pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtr; +pub use crate::vector::RealVector; +pub use crate::vector::VectorIntoIterator; pub use cxxbridge_macro::bridge; // Not public API. @@ -390,6 +395,7 @@ pub mod private { pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; + pub use crate::vector::VectorTarget; } use crate::error::Result; diff --git a/src/rust_vec.rs b/src/rust_vec.rs new file mode 100644 index 0000000..a28570d --- /dev/null +++ b/src/rust_vec.rs @@ -0,0 +1,39 @@ +use crate::vector::RealVector; +use crate::vector::VectorTarget; + +#[repr(C)] +pub struct RustVec> { + repr: Vec, +} + +impl> RustVec { + pub fn from(v: Vec) -> Self { + RustVec { repr: v } + } + + pub fn from_ref(v: &Vec) -> &Self { + unsafe { std::mem::transmute::<&Vec, &RustVec>(v) } + } + + pub fn into_vec(self) -> Vec { + self.repr + } + + pub fn as_vec(&self) -> &Vec { + &self.repr + } + + pub fn as_mut_vec(&mut self) -> &mut Vec { + &mut self.repr + } + + pub fn len(&self) -> usize { + self.repr.len() + } + + pub fn into_vector(&self, vec: &mut RealVector) { + for item in &self.repr { + vec.push_back(item); + } + } +} diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 0000000..805bdbb --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,91 @@ +pub trait VectorTarget { + fn get_unchecked(v: &RealVector, pos: usize) -> &T + where + Self: Sized; + fn vector_length(v: &RealVector) -> usize + where + Self: Sized; + fn push_back(v: &RealVector, item: &T) + where + Self: Sized; +} + +/// Binding to C++ `std::vector`. +/// +/// # Invariants +/// +/// As an invariant of this API and the static analysis of the cxx::bridge +/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector +/// requires a move constructor and may hold internal pointers, which is not +/// compatible with Rust's move behavior. Instead in Rust code we will only ever +/// look at a Vector through a reference or smart pointer, as in `&Vector` +/// or `UniquePtr`. +#[repr(C)] +pub struct RealVector { + _private: [T; 0], +} + +impl> RealVector { + /// Returns the length of the vector in bytes. + pub fn size(&self) -> usize { + T::vector_length(self) + } + + pub fn get_unchecked(&self, pos: usize) -> &T { + T::get_unchecked(self, pos) + } + + /// Returns true if `self` has a length of zero bytes. + pub fn is_empty(&self) -> bool { + self.size() == 0 + } + + pub fn get(&self, pos: usize) -> Option<&T> { + if pos < self.size() { + Some(self.get_unchecked(pos)) + } else { + None + } + } + + pub fn push_back(&mut self, item: &T) { + T::push_back(self, item); + } +} + +unsafe impl Send for RealVector where T: Send + VectorTarget {} + +pub struct VectorIntoIterator<'a, T> { + v: &'a RealVector, + index: usize, +} + +impl<'a, T: VectorTarget> IntoIterator for &'a RealVector { + type Item = &'a T; + type IntoIter = VectorIntoIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + VectorIntoIterator { v: self, index: 0 } + } +} + +impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.index = self.index + 1; + self.v.get(self.index - 1) + } +} + +cxxbridge_macro::vector_builtin!(u8); +cxxbridge_macro::vector_builtin!(u16); +cxxbridge_macro::vector_builtin!(u32); +cxxbridge_macro::vector_builtin!(u64); +cxxbridge_macro::vector_builtin!(usize); +cxxbridge_macro::vector_builtin!(i8); +cxxbridge_macro::vector_builtin!(i16); +cxxbridge_macro::vector_builtin!(i32); +cxxbridge_macro::vector_builtin!(i64); +cxxbridge_macro::vector_builtin!(isize); +cxxbridge_macro::vector_builtin!(f32); +cxxbridge_macro::vector_builtin!(f64); diff --git a/syntax/atom.rs b/syntax/atom.rs index eeea831..c68b3fe 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -42,6 +42,43 @@ impl Atom { _ => None, } } + + pub fn to_cxx(&self) -> &'static str { + use self::Atom::*; + match self { + Bool => "bool", + U8 => "uint8_t", + U16 => "uint16_t", + U32 => "uint32_t", + U64 => "uint64_t", + Usize => "size_t", + I8 => "int8_t", + I16 => "int16_t", + I32 => "int32_t", + I64 => "int64_t", + Isize => "::rust::isize", + F32 => "float", + F64 => "double", + CxxString => "::std::string", + RustString => "::rust::String", + } + } + + pub fn is_valid_vector_target(&self) -> bool { + use self::Atom::*; + *self == U8 + || *self == U16 + || *self == U32 + || *self == U64 + || *self == Usize + || *self == I8 + || *self == I16 + || *self == I32 + || *self == I64 + || *self == Isize + || *self == F32 + || *self == F64 + } } impl PartialEq for Ident { diff --git a/syntax/check.rs b/syntax/check.rs index 5e8cc09..387f9a6 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,7 +29,9 @@ fn do_typecheck(cx: &mut Check) { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), + Type::RustVec(ptr) => check_type_vec(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), + Type::Vector(ptr) => check_type_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Slice(ty) => check_type_slice(cx, ty), _ => {} @@ -87,6 +89,21 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported target type of Box"); } +fn check_type_vec(cx: &mut Check, ptr: &Ty1) { + // Vec can contain either user-defined type or u8 + if let Type::Ident(ident) = &ptr.inner { + if Atom::from(ident).map(|a| a.is_valid_vector_target()) == Some(true) { + return; + } else if cx.types.cxx.contains(ident) { + cx.error(ptr, error::VEC_CXX_TYPE.msg); + } else { + return; + } + } + + cx.error(ptr, "unsupported target 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) { @@ -97,11 +114,31 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(CxxString) => return, _ => {} } + } else if let Type::Vector(_) = &ptr.inner { + return; } cx.error(ptr, "unsupported unique_ptr target type"); } +fn check_type_vector(cx: &mut Check, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if cx.types.rust.contains(ident) { + cx.error(ptr, "vector of a Rust type is not supported yet"); + } + + match Atom::from(ident) { + None => return, + Some(atom) => { + if atom.is_valid_vector_target() { + return; + } + } + } + } + cx.error(ptr, "unsupported vector target type"); +} + fn check_type_ref(cx: &mut Check, ty: &Ref) { if ty.lifetime.is_some() { cx.error(ty, "references with explicit lifetimes are not supported"); @@ -310,9 +347,11 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } } Type::RustBox(_) => "Box".to_owned(), + Type::RustVec(_) => "Vec".to_owned(), Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Vector(_) => "vector".to_owned(), Type::Slice(_) => "slice".to_owned(), Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), diff --git a/syntax/error.rs b/syntax/error.rs index f52d651..103a54f 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -15,6 +15,7 @@ impl Display for Error { pub static ERRORS: &[Error] = &[ BOX_CXX_TYPE, + VEC_CXX_TYPE, CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, @@ -29,6 +30,12 @@ pub static BOX_CXX_TYPE: Error = Error { note: Some("hint: use UniquePtr<>"), }; +pub static VEC_CXX_TYPE: Error = Error { + msg: "Vec of a C++ type is not supported yet", + label: None, + note: Some("hint: use UniquePtr<>"), +}; + pub static CXXBRIDGE_RESERVED: Error = Error { msg: "identifiers starting with cxxbridge are reserved", label: Some("reserved identifier"), diff --git a/syntax/impls.rs b/syntax/impls.rs index a2ce05b..8e94f06 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -26,6 +26,8 @@ impl Hash for Type { Type::UniquePtr(t) => t.hash(state), Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), + Type::RustVec(t) => t.hash(state), + Type::Vector(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::Slice(t) => t.hash(state), Type::SliceRefU8(t) => t.hash(state), @@ -44,6 +46,8 @@ impl PartialEq for Type { (Type::UniquePtr(lhs), Type::UniquePtr(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::Vector(lhs), Type::Vector(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, diff --git a/syntax/mangled.rs b/syntax/mangled.rs new file mode 100644 index 0000000..56e8b73 --- /dev/null +++ b/syntax/mangled.rs @@ -0,0 +1,30 @@ +use crate::syntax::{Atom, Type}; + +pub trait ToMangled { + fn to_mangled(&self, namespace: &Vec) -> String; +} + +impl ToMangled for Type { + fn to_mangled(&self, namespace: &Vec) -> String { + match self { + Type::Ident(ident) => { + let mut instance = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + instance += name; + instance += "$"; + } + } + instance += &ident.to_string(); + instance + } + Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)), + Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)), + Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)), + Type::Vector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), + _ => unimplemented!(), + } + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index ab5cecc..4e0b908 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -8,11 +8,13 @@ pub mod error; pub mod ident; mod impls; pub mod mangle; +pub mod mangled; pub mod namespace; mod parse; pub mod set; pub mod symbol; mod tokens; +pub mod typename; pub mod types; use self::parse::kw; @@ -86,9 +88,11 @@ pub struct Receiver { pub enum Type { Ident(Ident), RustBox(Box), + RustVec(Box), UniquePtr(Box), Ref(Box), Str(Box), + Vector(Box), Fn(Box), Void(Span), Slice(Box), diff --git a/syntax/namespace.rs b/syntax/namespace.rs index d26bb9e..a4e972b 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -11,7 +11,7 @@ mod kw { #[derive(Clone)] pub struct Namespace { - segments: Vec, + pub segments: Vec, } impl Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index 7d92b1b..f5c598d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,6 +300,16 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Vector" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::Vector(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; @@ -310,6 +320,16 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Vec" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::RustVec(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } } } PathArguments::Parenthesized(_) => {} diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 26bb3d1..3d67a0a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -14,7 +14,9 @@ impl ToTokens for Type { } ident.to_tokens(tokens); } - Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + ty.to_tokens(tokens) + } Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), Type::Slice(s) => s.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), @@ -33,7 +35,8 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { - if self.name == "UniquePtr" { + // Do not add cxx namespace to Vector since we're defining it in the user crate + if self.name == "UniquePtr" || self.name == "RustVec" { let span = self.name.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } diff --git a/syntax/typename.rs b/syntax/typename.rs new file mode 100644 index 0000000..883e1fb --- /dev/null +++ b/syntax/typename.rs @@ -0,0 +1,36 @@ +use crate::syntax::{Atom, Type}; + +pub trait ToTypename { + fn to_typename(&self, namespace: &Vec) -> String; +} + +impl ToTypename for Type { + fn to_typename(&self, namespace: &Vec) -> String { + match self { + Type::Ident(ident) => { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + inner += name; + inner += "::"; + } + } + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + inner + } + Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)), + Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)), + Type::UniquePtr(ptr) => { + format!("std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) + } + Type::Vector(ptr) => format!("std::vector<{}>", ptr.inner.to_typename(namespace)), + _ => unimplemented!(), + } + } +} diff --git a/syntax/types.rs b/syntax/types.rs index 6f3af09..7bce154 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -24,7 +24,9 @@ impl<'a> Types<'a> { all.insert(ty); match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} - Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + visit(all, &ty.inner) + } Type::Ref(r) => visit(all, &r.inner), Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index efdd1fa..27f5f6d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -27,6 +27,9 @@ pub mod ffi { fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; + fn c_return_unique_ptr_vector_u8() -> UniquePtr>; + fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -38,11 +41,19 @@ pub mod ffi { fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + fn c_take_unique_ptr_vector_u8(s: UniquePtr>); + fn c_take_unique_ptr_vector_f64(s: UniquePtr>); + fn c_take_unique_ptr_vector_shared(s: UniquePtr>); + + fn c_take_vec_u8(v: &Vec); + fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; fn c_fail_return_primitive() -> Result; + fn c_try_return_string() -> Result>; + fn c_fail_return_string() -> Result>; fn c_try_return_box() -> Result>; fn c_try_return_ref(s: &String) -> Result<&String>; fn c_try_return_str(s: &str) -> Result<&str>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 619485a..9f11d9f 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,3 +1,4 @@ +#include #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs.h" #include @@ -57,6 +58,31 @@ std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); } +std::unique_ptr> c_return_unique_ptr_vector_u8() { + auto retval = std::unique_ptr>(new std::vector()); + retval->push_back(86); + retval->push_back(75); + retval->push_back(30); + retval->push_back(9); + return retval; +} + +std::unique_ptr> c_return_unique_ptr_vector_f64() { + auto retval = std::unique_ptr>(new std::vector()); + retval->push_back(86.0); + retval->push_back(75.0); + retval->push_back(30.0); + retval->push_back(9.5); + return retval; +} + +std::unique_ptr> c_return_unique_ptr_vector_shared() { + auto retval = std::unique_ptr>(new std::vector()); + retval->push_back(Shared{1010}); + retval->push_back(Shared{1011}); + return retval; +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -118,6 +144,43 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_take_unique_ptr_vector_u8(std::unique_ptr> v) { + if (v->size() == 4) { + cxx_test_suite_set_correct(); + } +} + +void c_take_unique_ptr_vector_f64(std::unique_ptr> v) { + if (v->size() == 4) { + cxx_test_suite_set_correct(); + } +} + +void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { + if (v->size() == 2) { + cxx_test_suite_set_correct(); + } +} + +void c_take_vec_u8(const ::rust::Vec& v) { + auto cv = static_cast>(v); + uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); + if (sum == 200) { + cxx_test_suite_set_correct(); + } +} + +void c_take_vec_shared(const ::rust::Vec& v) { + auto cv = static_cast>(v); + uint32_t sum = 0; + for (auto i: cv) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + void c_take_callback(rust::Fn callback) { callback("2020"); } @@ -128,6 +191,14 @@ size_t c_try_return_primitive() { return 2020; } size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } +std::unique_ptr c_try_return_string() { + return std::unique_ptr(new std::string("ok")); +} + +std::unique_ptr c_fail_return_string() { + throw std::logic_error("logic error getting string"); +} + rust::Box c_try_return_box() { return c_return_box(); } const rust::String &c_try_return_ref(const rust::String &s) { return s; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index ed6d541..9e75c37 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -29,6 +29,9 @@ rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); +std::unique_ptr> c_return_unique_ptr_vector_u8(); +std::unique_ptr> c_return_unique_ptr_vector_f64(); +std::unique_ptr> c_return_unique_ptr_vector_shared(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -40,11 +43,19 @@ void c_take_str(rust::Str s); void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_take_unique_ptr_vector_u8(std::unique_ptr> v); +void c_take_unique_ptr_vector_f64(std::unique_ptr> v); +void c_take_unique_ptr_vector_shared(std::unique_ptr> v); + +void c_take_vec_u8(const ::rust::Vec& v); +void c_take_vec_shared(const ::rust::Vec& v); void c_take_callback(rust::Fn callback); void c_try_return_void(); size_t c_try_return_primitive(); size_t c_fail_return_primitive(); +std::unique_ptr c_try_return_string(); +std::unique_ptr c_fail_return_string(); rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); diff --git a/tests/test.rs b/tests/test.rs index 1cd85df..17fcfca 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -32,6 +32,45 @@ fn test_c_return() { assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); + assert_eq!( + 4, + ffi::c_return_unique_ptr_vector_u8() + .as_ref() + .unwrap() + .size() + ); + assert_eq!( + 200_u8, + ffi::c_return_unique_ptr_vector_u8() + .as_ref() + .unwrap() + .into_iter() + .sum() + ); + assert_eq!( + 200.5_f64, + ffi::c_return_unique_ptr_vector_f64() + .as_ref() + .unwrap() + .into_iter() + .sum() + ); + assert_eq!( + 2, + ffi::c_return_unique_ptr_vector_shared() + .as_ref() + .unwrap() + .size() + ); + assert_eq!( + 2021_usize, + ffi::c_return_unique_ptr_vector_shared() + .as_ref() + .unwrap() + .into_iter() + .map(|o| o.z) + .sum() + ); } #[test] @@ -42,6 +81,18 @@ fn test_c_try_return() { "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); + assert_eq!( + "ok", + ffi::c_try_return_string() + .unwrap() + .as_ref() + .unwrap() + .to_string() + ); + assert_eq!( + "logic error getting string", + ffi::c_fail_return_string().unwrap_err().what(), + ); assert_eq!(2020, *ffi::c_try_return_box().unwrap()); assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); @@ -65,6 +116,22 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string() )); + check!(ffi::c_take_unique_ptr_vector_u8( + ffi::c_return_unique_ptr_vector_u8() + )); + check!(ffi::c_take_unique_ptr_vector_f64( + ffi::c_return_unique_ptr_vector_f64() + )); + check!(ffi::c_take_unique_ptr_vector_shared( + ffi::c_return_unique_ptr_vector_shared() + )); + + check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); + + check!(ffi::c_take_vec_shared(&vec![ + ffi::Shared { z: 1010 }, + ffi::Shared { z: 1011 } + ])); } #[test] diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 2e70499..4552f46 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -54,6 +54,15 @@ dependencies = [ ] [[package]] +name = "codespan" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" +dependencies = [ + "unicode-segmentation", +] + +[[package]] name = "codespan-reporting" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -69,6 +78,7 @@ version = "0.2.10" dependencies = [ "anyhow", "cc", + "codespan", "codespan-reporting", "cxx-test-suite", "cxxbridge-macro", From 37dd7e11742d7fd0d7d67813e520186625281986 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 25 2020 19:51:59 +0000 Subject: [PATCH 356/2232] Format with clang-format --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 387c56a..0274ff1 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -24,7 +24,7 @@ std::unique_ptr> do_thing(SharedThing state) { return vec; } -JsonBlob get_jb(const ::rust::Vec& vec) { +JsonBlob get_jb(const ::rust::Vec &vec) { JsonBlob retval; std::cout << "incoming vec length is " << vec.size() << "\n"; diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index eea0af7..29a3ba1 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -20,7 +20,7 @@ struct JsonBlob; std::unique_ptr make_demo(::rust::Str appname); const std::string &get_name(const ThingC &thing); std::unique_ptr> do_thing(SharedThing state); -JsonBlob get_jb(const ::rust::Vec& vec); +JsonBlob get_jb(const ::rust::Vec &vec); } // namespace example } // namespace org diff --git a/include/cxx.h b/include/cxx.h index 35e9059..0a664d0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -5,9 +5,9 @@ #include #include #include -#include #include #include +#include #if defined(_WIN32) #include #endif @@ -97,7 +97,7 @@ private: Vec(const Vec &other) noexcept; Vec &operator=(Vec other) noexcept; void drop() noexcept; - + // Repr const T *ptr; size_t len; diff --git a/src/cxx.cc b/src/cxx.cc index 916f525..a0e92ac 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include template static void panic [[noreturn]] (const char *msg) { @@ -199,38 +199,56 @@ void cxxbridge02$unique_ptr$std$string$drop( } } // extern "C" -#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ -extern "C" { \ -size_t cxxbridge02$std$vector$##RUST_TYPE##$length(const std::vector &s) noexcept { \ - return s.size(); \ -} \ -void cxxbridge02$std$vector$##RUST_TYPE##$push_back(std::vector &s, const CXX_TYPE &item) noexcept { \ - s.push_back(item); \ -} \ -const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked(const std::vector &s, size_t pos) noexcept { \ - return &s[pos]; \ -} \ -static_assert(sizeof(::std::unique_ptr>) == sizeof(void *), ""); \ -static_assert(alignof(::std::unique_ptr>) == alignof(void *), ""); \ -void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null(::std::unique_ptr> *ptr) noexcept { \ - new (ptr) ::std::unique_ptr>(); \ -} \ -void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new(::std::unique_ptr> *ptr, std::vector *value) noexcept { \ - new (ptr) ::std::unique_ptr>(new std::vector(::std::move(*value))); \ -} \ -void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw(::std::unique_ptr> *ptr, std::vector *raw) noexcept { \ - new (ptr) ::std::unique_ptr>(raw); \ -} \ -const std::vector *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get(const ::std::unique_ptr>& ptr) noexcept { \ - return ptr.get(); \ -} \ -std::vector *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release(::std::unique_ptr>& ptr) noexcept { \ - return ptr.release(); \ -} \ -void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop(::std::unique_ptr> *ptr) noexcept { \ - ptr->~unique_ptr(); \ -} \ -} // extern "C" +#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ + extern "C" { \ + size_t cxxbridge02$std$vector$##RUST_TYPE##$length( \ + const std::vector &s) noexcept { \ + return s.size(); \ + } \ + void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ + std::vector &s, const CXX_TYPE &item) noexcept { \ + s.push_back(item); \ + } \ + const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ + const std::vector &s, size_t pos) noexcept { \ + return &s[pos]; \ + } \ + static_assert(sizeof(::std::unique_ptr>) == \ + sizeof(void *), \ + ""); \ + static_assert(alignof(::std::unique_ptr>) == \ + alignof(void *), \ + ""); \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ + ::std::unique_ptr> *ptr) noexcept { \ + new (ptr)::std::unique_ptr>(); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new( \ + ::std::unique_ptr> *ptr, \ + std::vector *value) noexcept { \ + new (ptr)::std::unique_ptr>( \ + new std::vector(::std::move(*value))); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + ::std::unique_ptr> *ptr, \ + std::vector *raw) noexcept { \ + new (ptr)::std::unique_ptr>(raw); \ + } \ + const std::vector * \ + cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const ::std::unique_ptr> &ptr) noexcept { \ + return ptr.get(); \ + } \ + std::vector * \ + cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ + ::std::unique_ptr> &ptr) noexcept { \ + return ptr.release(); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + ::std::unique_ptr> *ptr) noexcept { \ + ptr->~unique_ptr(); \ + } \ + } // extern "C" STD_VECTOR_OPS(u8, uint8_t); STD_VECTOR_OPS(u16, uint16_t); @@ -244,4 +262,3 @@ STD_VECTOR_OPS(i64, int64_t); STD_VECTOR_OPS(isize, rust::isize); STD_VECTOR_OPS(f32, float); STD_VECTOR_OPS(f64, double); - diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 9f11d9f..446486d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,7 +1,7 @@ -#include #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs.h" #include +#include #include extern "C" void cxx_test_suite_set_correct() noexcept; @@ -59,7 +59,8 @@ std::unique_ptr c_return_unique_ptr_string() { } std::unique_ptr> c_return_unique_ptr_vector_u8() { - auto retval = std::unique_ptr>(new std::vector()); + auto retval = + std::unique_ptr>(new std::vector()); retval->push_back(86); retval->push_back(75); retval->push_back(30); @@ -162,7 +163,7 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { } } -void c_take_vec_u8(const ::rust::Vec& v) { +void c_take_vec_u8(const ::rust::Vec &v) { auto cv = static_cast>(v); uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); if (sum == 200) { @@ -170,10 +171,10 @@ void c_take_vec_u8(const ::rust::Vec& v) { } } -void c_take_vec_shared(const ::rust::Vec& v) { +void c_take_vec_shared(const ::rust::Vec &v) { auto cv = static_cast>(v); uint32_t sum = 0; - for (auto i: cv) { + for (auto i : cv) { sum += i.z; } if (sum == 2021) { @@ -195,8 +196,8 @@ std::unique_ptr c_try_return_string() { return std::unique_ptr(new std::string("ok")); } -std::unique_ptr c_fail_return_string() { - throw std::logic_error("logic error getting string"); +std::unique_ptr c_fail_return_string() { + throw std::logic_error("logic error getting string"); } rust::Box c_try_return_box() { return c_return_box(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 9e75c37..d2ce44e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -47,8 +47,8 @@ void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); -void c_take_vec_u8(const ::rust::Vec& v); -void c_take_vec_shared(const ::rust::Vec& v); +void c_take_vec_u8(const ::rust::Vec &v); +void c_take_vec_shared(const ::rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); From 7c29546739e60197982bfe9d229ca7ac4d493475 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 25 2020 20:13:43 +0000 Subject: [PATCH 357/2232] Revert some unrelated changes from PR 67 --- diff --git a/Cargo.toml b/Cargo.toml index 95cd87d..f497482 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" cc = "1.0.49" -codespan = "0.7" codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.10", path = "macro" } link-cplusplus = "1.0" diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 0274ff1..4940834 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -9,7 +9,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } -std::unique_ptr make_demo(::rust::Str appname) { +std::unique_ptr make_demo(rust::Str appname) { return std::unique_ptr(new ThingC(std::string(appname))); } diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 29a3ba1..2c9f1c0 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -17,7 +17,7 @@ public: struct SharedThing; struct JsonBlob; -std::unique_ptr make_demo(::rust::Str appname); +std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); std::unique_ptr> do_thing(SharedThing state); JsonBlob get_jb(const ::rust::Vec &vec); diff --git a/gen/write.rs b/gen/write.rs index a74833f..8a60f17 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -44,7 +44,7 @@ pub(super) fn gen( Api::Struct(strct) => write_struct_decl(out, &strct.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident), Api::RustType(ety) => write_struct_decl(out, &ety.ident), - _ => (), + _ => {} } } @@ -127,7 +127,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { Type::UniquePtr(_) => out.include.memory = true, Type::Vector(_) => out.include.vector = true, Type::SliceRefU8(_) => out.include.cstdint = true, - _ => (), + _ => {} } } } @@ -139,6 +139,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_box = false; let mut needs_rust_vec = false; let mut needs_rust_fn = false; + let mut needs_rust_isize = false; for ty in types { match ty { Type::RustBox(_) => { @@ -160,6 +161,10 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Type::Slice(_) | Type::SliceRefU8(_) => { needs_rust_slice = true; } + ty if ty == Isize => { + out.include.base_tsd = true; + needs_rust_isize = true; + } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; @@ -219,6 +224,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_rust_vec || needs_rust_fn || needs_rust_error + || needs_rust_isize || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit @@ -239,6 +245,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); if needs_manually_drop { diff --git a/include/cxx.h b/include/cxx.h index 0a664d0..31b3dde 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -256,11 +256,14 @@ private: }; #endif // CXXBRIDGE02_RUST_ERROR +#ifndef CXXBRIDGE02_RUST_ISIZE +#define CXXBRIDGE02_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif +#endif // CXXBRIDGE02_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 99ae923..ca0e326 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -19,7 +19,7 @@ quote = "1.0" syn = { version = "1.0", features = ["full"] } [dev-dependencies] -cxx = { version = "0.2.7-alpha-1", path = ".." } +cxx = { version = "0.2", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 27f5f6d..da0040a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -44,7 +44,6 @@ pub mod ffi { fn c_take_unique_ptr_vector_u8(s: UniquePtr>); fn c_take_unique_ptr_vector_f64(s: UniquePtr>); fn c_take_unique_ptr_vector_shared(s: UniquePtr>); - fn c_take_vec_u8(v: &Vec); fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); @@ -52,8 +51,6 @@ pub mod ffi { fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; fn c_fail_return_primitive() -> Result; - fn c_try_return_string() -> Result>; - fn c_fail_return_string() -> Result>; fn c_try_return_box() -> Result>; fn c_try_return_ref(s: &String) -> Result<&String>; fn c_try_return_str(s: &str) -> Result<&str>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 446486d..a8d7018 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -192,14 +192,6 @@ size_t c_try_return_primitive() { return 2020; } size_t c_fail_return_primitive() { throw std::logic_error("logic error"); } -std::unique_ptr c_try_return_string() { - return std::unique_ptr(new std::string("ok")); -} - -std::unique_ptr c_fail_return_string() { - throw std::logic_error("logic error getting string"); -} - rust::Box c_try_return_box() { return c_return_box(); } const rust::String &c_try_return_ref(const rust::String &s) { return s; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index d2ce44e..876e3c5 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -46,7 +46,6 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); - void c_take_vec_u8(const ::rust::Vec &v); void c_take_vec_shared(const ::rust::Vec &v); void c_take_callback(rust::Fn callback); @@ -54,8 +53,6 @@ void c_take_callback(rust::Fn callback); void c_try_return_void(); size_t c_try_return_primitive(); size_t c_fail_return_primitive(); -std::unique_ptr c_try_return_string(); -std::unique_ptr c_fail_return_string(); rust::Box c_try_return_box(); const rust::String &c_try_return_ref(const rust::String &); rust::Str c_try_return_str(rust::Str); diff --git a/tests/test.rs b/tests/test.rs index 17fcfca..b33c675 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -81,18 +81,6 @@ fn test_c_try_return() { "logic error", ffi::c_fail_return_primitive().unwrap_err().what(), ); - assert_eq!( - "ok", - ffi::c_try_return_string() - .unwrap() - .as_ref() - .unwrap() - .to_string() - ); - assert_eq!( - "logic error getting string", - ffi::c_fail_return_string().unwrap_err().what(), - ); assert_eq!(2020, *ffi::c_try_return_box().unwrap()); assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap()); assert_eq!("2020", ffi::c_try_return_str("2020").unwrap()); @@ -125,9 +113,7 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_vector_shared( ffi::c_return_unique_ptr_vector_shared() )); - check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); - check!(ffi::c_take_vec_shared(&vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 4552f46..2e70499 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -54,15 +54,6 @@ dependencies = [ ] [[package]] -name = "codespan" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5" -dependencies = [ - "unicode-segmentation", -] - -[[package]] name = "codespan-reporting" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -78,7 +69,6 @@ version = "0.2.10" dependencies = [ "anyhow", "cc", - "codespan", "codespan-reporting", "cxx-test-suite", "cxxbridge-macro", From 507c2d75cfb929b7c153a0f2eb11529cb085dd82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:01:54 +0000 Subject: [PATCH 358/2232] Merge pull request #67 from myronahn/master C++ std::vector and Rust std::vec::Vec support --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..4940834 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -15,7 +15,35 @@ std::unique_ptr make_demo(rust::Str appname) { const std::string &get_name(const ThingC &thing) { return thing.appname; } -void do_thing(SharedThing state) { print_r(*state.y); } +std::unique_ptr> do_thing(SharedThing state) { + print_r(*state.y); + auto vec = std::unique_ptr>(new std::vector()); + for (uint8_t i = 0; i < 10; i++) { + vec->push_back(i * i); + } + return vec; +} + +JsonBlob get_jb(const ::rust::Vec &vec) { + JsonBlob retval; + + std::cout << "incoming vec length is " << vec.size() << "\n"; + auto vec_copy = static_cast>(vec); + std::cout << "vec_copy length is " << vec_copy.size() << "\n"; + std::cout << "vec_copy[0] is " << (int)vec_copy[0] << "\n"; + + auto blob = std::unique_ptr>(new std::vector()); + for (uint8_t i = 0; i < 10; i++) { + blob->push_back(i * 2); + } + + auto json = std::unique_ptr(new std::string("{\"demo\": 23}")); + + retval.json = std::move(json); + retval.blob = std::move(blob); + + return retval; +} } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index fafc474..2c9f1c0 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -15,10 +15,12 @@ public: }; struct SharedThing; +struct JsonBlob; std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); -void do_thing(SharedThing state); +std::unique_ptr> do_thing(SharedThing state); +JsonBlob get_jb(const ::rust::Vec &vec); } // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 66dfc79..8bf9926 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -6,13 +6,19 @@ mod ffi { x: UniquePtr, } + struct JsonBlob { + json: UniquePtr, + blob: UniquePtr>, + } + extern "C" { include!("demo-cxx/demo.h"); type ThingC; fn make_demo(appname: &str) -> UniquePtr; fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing); + fn do_thing(state: SharedThing) -> UniquePtr>; + fn get_jb(v: &Vec) -> JsonBlob; } extern "Rust" { @@ -31,9 +37,24 @@ fn main() { let x = ffi::make_demo("demo of cxx::bridge"); println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - ffi::do_thing(ffi::SharedThing { + let vec = ffi::do_thing(ffi::SharedThing { z: 222, y: Box::new(ThingR(333)), x, }); + + println!("vec length = {}", vec.as_ref().unwrap().size()); + for (i, v) in vec.as_ref().unwrap().into_iter().enumerate() { + println!("vec[{}] = {}", i, v); + } + + let mut rv: Vec = Vec::new(); + for _ in 0..1000 { + rv.push(33); + } + let jb = ffi::get_jb(&rv); + println!("json: {}", jb.json.as_ref().unwrap()); + for (i, v) in jb.blob.as_ref().unwrap().into_iter().enumerate() { + println!("jb.blob[{}] = {}", i, v); + } } diff --git a/gen/include.rs b/gen/include.rs index 8f38fe3..077d03e 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -36,6 +36,7 @@ pub struct Includes { pub exception: bool, pub memory: bool, pub string: bool, + pub vector: bool, pub type_traits: bool, pub utility: bool, pub base_tsd: bool, diff --git a/gen/write.rs b/gen/write.rs index a8e3fd4..8a60f17 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,8 +1,10 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; +use crate::syntax::typename::ToTypename; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -123,6 +125,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, + Type::Vector(_) => out.include.vector = true, Type::SliceRefU8(_) => out.include.cstdint = true, _ => {} } @@ -134,6 +137,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_str = false; let mut needs_rust_slice = false; let mut needs_rust_box = false; + let mut needs_rust_vec = false; let mut needs_rust_fn = false; let mut needs_rust_isize = false; for ty in types { @@ -142,6 +146,10 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.type_traits = true; needs_rust_box = true; } + Type::RustVec(_) => { + out.include.type_traits = true; + needs_rust_vec = true; + } Type::Str(_) => { out.include.cstdint = true; out.include.string = true; @@ -213,6 +221,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_rust_str || needs_rust_slice || needs_rust_box + || needs_rust_vec || needs_rust_fn || needs_rust_error || needs_rust_isize @@ -233,6 +242,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); @@ -469,6 +479,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::Vector(_)) => write!( + out, + " /* Use RVO to convert to r-value and move construct */" + ), Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } @@ -779,7 +793,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -796,21 +810,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), + Some(a) => write!(out, "{}", a.to_cxx()), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { @@ -818,11 +818,21 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, ">"); } + Type::RustVec(ty) => { + write!(out, "::rust::Vec<"); + write_type(out, &ty.inner); + write!(out, ">"); + } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); write_type(out, &ptr.inner); write!(out, ">"); } + Type::Vector(ty) => { + write!(out, "::std::vector<"); + write_type(out, &ty.inner); + write!(out, ">"); + } Type::Ref(r) => { if r.mutability.is_none() { write!(out, "const "); @@ -870,6 +880,8 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) + | Type::Vector(_) + | Type::RustVec(_) | Type::SliceRefU8(_) | Type::Fn(_) => write!(out, " "), Type::Ref(_) => {} @@ -882,6 +894,11 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { Atom::from(ident).is_none() } + fn allow_vector(ident: &Ident) -> bool { + // Note: built-in types such as u8 are already defined in cxx.cc + Atom::from(ident).is_none() + } + out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -889,11 +906,30 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.next_section(); write_rust_box_extern(out, inner); } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(_) = &ty.inner { + out.next_section(); + write_rust_vec_extern(out, &ty.inner); + } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, inner, types); + write_unique_ptr(out, &ptr.inner, types); + } + } else if let Type::Vector(ptr1) = &ptr.inner { + if let Type::Ident(inner) = &ptr1.inner { + if allow_vector(inner) { + out.next_section(); + write_unique_ptr(out, &ptr.inner, types); + } + } + } + } else if let Type::Vector(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if allow_vector(inner) { + out.next_section(); + write_vector(out, inner); } } } @@ -907,6 +943,10 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ty.inner { write_rust_box_impl(out, inner); } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(_) = &ty.inner { + write_rust_vec_impl(out, &ty.inner); + } } } out.end_block("namespace cxxbridge02"); @@ -937,6 +977,31 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); } +fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!( + out, + "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const std::vector<{}> &vector) noexcept;", + instance, inner, inner + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); +} + fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { let mut inner = String::new(); for name in &out.namespace { @@ -957,16 +1022,44 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { - out.include.utility = true; +fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); + writeln!(out, "template <>"); + writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$drop(this);", + instance + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!( + out, + "Vec<{}>::operator std::vector<{}>() const noexcept {{", + inner, inner + ); + writeln!( + out, + " std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", + inner, instance, + ); + writeln!(out, "}}"); +} + +fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.utility = true; + let namespace = out.namespace.iter().cloned().collect::>(); + let inner = ty.to_typename(&namespace); + let instance = ty.to_mangled(&namespace); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); @@ -987,18 +1080,21 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - if types.structs.contains_key(ident) { - writeln!( + match ty { + Type::Ident(ident) if types.structs.contains_key(ident) => { + writeln!( out, "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } + _ => (), } writeln!( out, @@ -1030,3 +1126,50 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { writeln!(out, "}}"); writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); } + +fn write_vector(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in &out.namespace { + inner += name; + inner += "::"; + } + } + let mut instance = inner.clone(); + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + instance += &ident.to_string(); + let instance = instance.replace("::", "$"); + + writeln!(out, "#ifndef CXXBRIDGE02_vector_{}", instance); + writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); + writeln!( + out, + "size_t cxxbridge02$std$vector${}$length(const std::vector<{}> &s) noexcept {{", + instance, inner, + ); + writeln!(out, " return s.size();"); + writeln!(out, "}}"); + + writeln!( + out, + "void cxxbridge02$std$vector${}$push_back(std::vector<{}> &s, const {} &item) noexcept {{", + instance, inner, inner + ); + writeln!(out, " s.push_back(item);"); + writeln!(out, "}}"); + + writeln!( + out, + "const {} *cxxbridge02$std$vector${}$get_unchecked(const std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return &s[pos];"); + writeln!(out, "}}"); + writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); +} diff --git a/include/cxx.h b/include/cxx.h index 7c2594c..8f03cbb 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -7,6 +7,7 @@ #include #include #include +#include #if defined(_WIN32) #include #endif @@ -83,6 +84,27 @@ private: }; #endif // CXXBRIDGE02_RUST_STR +#ifndef CXXBRIDGE02_RUST_VEC +#define CXXBRIDGE02_RUST_VEC +template +class Vec final { +public: + size_t size() const noexcept; + explicit operator std::vector() const noexcept; + +private: + Vec() noexcept; + Vec(const Vec &other) noexcept; + Vec &operator=(Vec other) noexcept; + void drop() noexcept; + + // Repr + const T *ptr; + size_t len; + size_t capacity; +}; +#endif // CXXBRIDGE02_RUST_VEC + #ifndef CXXBRIDGE02_RUST_SLICE #define CXXBRIDGE02_RUST_SLICE template diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8223572..95a6f1a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,12 +1,14 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; +use crate::syntax::typename::ToTypename; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, Error, ItemMod, Result, Token}; +use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -21,6 +23,38 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); + // "Header" to define newtypes locally so we can implement + // traits on them. + expanded.extend(quote! { + pub struct Vector(pub ::cxx::RealVector); + impl> Vector { + pub fn size(&self) -> usize { + self.0.size() + } + pub fn get(&self, pos: usize) -> Option<&T> { + self.0.get(pos) + } + pub fn get_unchecked(&self, pos: usize) -> &T { + self.0.get_unchecked(pos) + } + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + pub fn push_back(&mut self, item: &T) { + self.0.push_back(item) + } + } + impl<'a, T: cxx::private::VectorTarget> IntoIterator for &'a Vector { + type Item = &'a T; + type IntoIter = <&'a ::cxx::RealVector as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + unsafe impl Send for Vector where T: Send + cxx::private::VectorTarget {} + }); + for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); @@ -53,10 +87,34 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { hidden.extend(expand_rust_box(namespace, ident)); } } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(ident) = &ty.inner { + hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, ident, types)); + expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); + } + } else if let Type::Vector(_) = &ptr.inner { + // Generate code for unique_ptr> if T is not an atom + // or if T is a primitive. + // Code for primitives is already generated + match Atom::from(ident) { + None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)), + Some(atom) => { + if atom.is_valid_vector_target() { + expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); + } + } + } + } + } else if let Type::Vector(ptr) = ty { + if let Type::Ident(ident) = &ptr.inner { + if Atom::from(ident).is_none() { + // Generate code for Vector if T is not an atom + // Code for atoms is already generated + expanded.extend(expand_vector(namespace, &ptr.inner)); } } } @@ -192,10 +250,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), + Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) } + Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)), _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -518,9 +578,42 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); +fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); + let link_drop = format!("{}drop", link_prefix); + let link_vector_from = format!("{}vector_from", link_prefix); + let link_len = format!("{}len", link_prefix); + + let local_prefix = format_ident!("{}__vec_", ident); + let local_drop = format_ident!("{}drop", local_prefix); + let local_vector_from = format_ident!("{}vector_from", local_prefix); + let local_len = format_ident!("{}len", local_prefix); + + let span = ty.span(); + quote_spanned! {span=> + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { + std::ptr::drop_in_place(this); + } + #[export_name = #link_vector_from] + unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) { + this.as_ref().unwrap().into_vector(vector.as_mut().unwrap()); + } + #[export_name = #link_len] + unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { + this.as_ref().unwrap().len() + } + } +} + +fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { + let name = ty.to_typename(&namespace.segments); + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$unique_ptr${}", mangled); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -528,8 +621,8 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) { - Some(quote! { + let new_method = match ty { + Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] @@ -539,13 +632,12 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe { __new(&mut repr, &mut value) } repr } - }) - } else { - None + }), + _ => None, }; quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #ident { + unsafe impl ::cxx::private::UniquePtrTarget for #inner { const __NAME: &'static str = #name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { @@ -560,7 +652,7 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] - fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident); + fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner); } let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); __raw(&mut repr, raw); @@ -569,14 +661,14 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self { extern "C" { #[link_name = #link_get] - fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident; + fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner; } __get(&repr) } unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self { extern "C" { #[link_name = #link_release] - fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident; + fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner; } __release(&mut repr) } @@ -591,6 +683,90 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok } } +fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { + let inner = ty; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$std$vector${}", mangled); + let link_length = format!("{}length", prefix); + let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_push_back = format!("{}push_back", prefix); + + quote! { + impl ::cxx::private::VectorTarget<#inner> for #inner { + fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner; + } + unsafe { + __get_unchecked(v, pos) + } + } + fn vector_length(v: &::cxx::RealVector<#inner>) -> usize { + unsafe { + extern "C" { + #[link_name = #link_length] + fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize; + } + __vector_length(v) + } + } + fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) { + unsafe { + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize; + } + __push_back(v, item); + } + } + } + } +} + +pub fn expand_vector_builtin(ident: Ident) -> TokenStream { + let ty = Type::Ident(ident); + let inner = &ty; + let namespace = Namespace { segments: vec![] }; + let mangled = ty.to_mangled(&namespace.segments) + "$"; + let prefix = format!("cxxbridge02$std$vector${}", mangled); + let link_length = format!("{}length", prefix); + let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_push_back = format!("{}push_back", prefix); + + quote! { + impl VectorTarget<#inner> for #inner { + fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner; + } + unsafe { + __get_unchecked(v, pos) + } + } + fn vector_length(v: &RealVector<#inner>) -> usize { + unsafe { + extern "C" { + #[link_name = #link_length] + fn __vector_length(_: &RealVector<#inner>) -> usize; + } + __vector_length(v) + } + } + fn push_back(v: &RealVector<#inner>, item: &#inner) { + unsafe { + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize; + } + __push_back(v, item); + } + } + } + } +} + fn expand_return_type(ret: &Option) -> TokenStream { match ret { Some(ret) => quote!(-> #ret), @@ -608,11 +784,16 @@ fn expand_extern_type(ty: &Type) -> TokenStream { match ty { Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString), Type::RustBox(ty) | Type::UniquePtr(ty) => { - let inner = &ty.inner; + let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) } + Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), + Type::RustVec(ty) => { + let inner = expand_extern_type(&ty.inner); + quote!(&::cxx::RustVec<#inner>) + } _ => quote!(#ty), }, Type::Str(_) => quote!(::cxx::private::RustStr), diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b56f58e..87fe405 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,7 +14,7 @@ mod syntax; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod}; +use syn::{parse_macro_input, Ident, ItemMod}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,3 +44,9 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +#[proc_macro] +pub fn vector_builtin(input: TokenStream) -> TokenStream { + let ident = parse_macro_input!(input as Ident); + expand::expand_vector_builtin(ident).into() +} diff --git a/src/cxx.cc b/src/cxx.cc index b64333c..a0e92ac 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -4,6 +4,7 @@ #include #include #include +#include template static void panic [[noreturn]] (const char *msg) { @@ -197,3 +198,67 @@ void cxxbridge02$unique_ptr$std$string$drop( ptr->~unique_ptr(); } } // extern "C" + +#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ + extern "C" { \ + size_t cxxbridge02$std$vector$##RUST_TYPE##$length( \ + const std::vector &s) noexcept { \ + return s.size(); \ + } \ + void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ + std::vector &s, const CXX_TYPE &item) noexcept { \ + s.push_back(item); \ + } \ + const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ + const std::vector &s, size_t pos) noexcept { \ + return &s[pos]; \ + } \ + static_assert(sizeof(::std::unique_ptr>) == \ + sizeof(void *), \ + ""); \ + static_assert(alignof(::std::unique_ptr>) == \ + alignof(void *), \ + ""); \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ + ::std::unique_ptr> *ptr) noexcept { \ + new (ptr)::std::unique_ptr>(); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new( \ + ::std::unique_ptr> *ptr, \ + std::vector *value) noexcept { \ + new (ptr)::std::unique_ptr>( \ + new std::vector(::std::move(*value))); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + ::std::unique_ptr> *ptr, \ + std::vector *raw) noexcept { \ + new (ptr)::std::unique_ptr>(raw); \ + } \ + const std::vector * \ + cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const ::std::unique_ptr> &ptr) noexcept { \ + return ptr.get(); \ + } \ + std::vector * \ + cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ + ::std::unique_ptr> &ptr) noexcept { \ + return ptr.release(); \ + } \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + ::std::unique_ptr> *ptr) noexcept { \ + ptr->~unique_ptr(); \ + } \ + } // extern "C" + +STD_VECTOR_OPS(u8, uint8_t); +STD_VECTOR_OPS(u16, uint16_t); +STD_VECTOR_OPS(u32, uint32_t); +STD_VECTOR_OPS(u64, uint64_t); +STD_VECTOR_OPS(usize, size_t); +STD_VECTOR_OPS(i8, int8_t); +STD_VECTOR_OPS(i16, int16_t); +STD_VECTOR_OPS(i32, int32_t); +STD_VECTOR_OPS(i64, int64_t); +STD_VECTOR_OPS(isize, rust::isize); +STD_VECTOR_OPS(f32, float); +STD_VECTOR_OPS(f64, double); diff --git a/src/lib.rs b/src/lib.rs index 304a87a..e23be69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -370,13 +370,18 @@ mod result; mod rust_sliceu8; mod rust_str; mod rust_string; +mod rust_vec; mod syntax; mod unique_ptr; mod unwind; +mod vector; pub use crate::cxx_string::CxxString; pub use crate::exception::Exception; +pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtr; +pub use crate::vector::RealVector; +pub use crate::vector::VectorIntoIterator; pub use cxxbridge_macro::bridge; // Not public API. @@ -390,6 +395,7 @@ pub mod private { pub use crate::rust_string::RustString; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; + pub use crate::vector::VectorTarget; } use crate::error::Result; diff --git a/src/rust_vec.rs b/src/rust_vec.rs new file mode 100644 index 0000000..a28570d --- /dev/null +++ b/src/rust_vec.rs @@ -0,0 +1,39 @@ +use crate::vector::RealVector; +use crate::vector::VectorTarget; + +#[repr(C)] +pub struct RustVec> { + repr: Vec, +} + +impl> RustVec { + pub fn from(v: Vec) -> Self { + RustVec { repr: v } + } + + pub fn from_ref(v: &Vec) -> &Self { + unsafe { std::mem::transmute::<&Vec, &RustVec>(v) } + } + + pub fn into_vec(self) -> Vec { + self.repr + } + + pub fn as_vec(&self) -> &Vec { + &self.repr + } + + pub fn as_mut_vec(&mut self) -> &mut Vec { + &mut self.repr + } + + pub fn len(&self) -> usize { + self.repr.len() + } + + pub fn into_vector(&self, vec: &mut RealVector) { + for item in &self.repr { + vec.push_back(item); + } + } +} diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 0000000..805bdbb --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,91 @@ +pub trait VectorTarget { + fn get_unchecked(v: &RealVector, pos: usize) -> &T + where + Self: Sized; + fn vector_length(v: &RealVector) -> usize + where + Self: Sized; + fn push_back(v: &RealVector, item: &T) + where + Self: Sized; +} + +/// Binding to C++ `std::vector`. +/// +/// # Invariants +/// +/// As an invariant of this API and the static analysis of the cxx::bridge +/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector +/// requires a move constructor and may hold internal pointers, which is not +/// compatible with Rust's move behavior. Instead in Rust code we will only ever +/// look at a Vector through a reference or smart pointer, as in `&Vector` +/// or `UniquePtr`. +#[repr(C)] +pub struct RealVector { + _private: [T; 0], +} + +impl> RealVector { + /// Returns the length of the vector in bytes. + pub fn size(&self) -> usize { + T::vector_length(self) + } + + pub fn get_unchecked(&self, pos: usize) -> &T { + T::get_unchecked(self, pos) + } + + /// Returns true if `self` has a length of zero bytes. + pub fn is_empty(&self) -> bool { + self.size() == 0 + } + + pub fn get(&self, pos: usize) -> Option<&T> { + if pos < self.size() { + Some(self.get_unchecked(pos)) + } else { + None + } + } + + pub fn push_back(&mut self, item: &T) { + T::push_back(self, item); + } +} + +unsafe impl Send for RealVector where T: Send + VectorTarget {} + +pub struct VectorIntoIterator<'a, T> { + v: &'a RealVector, + index: usize, +} + +impl<'a, T: VectorTarget> IntoIterator for &'a RealVector { + type Item = &'a T; + type IntoIter = VectorIntoIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + VectorIntoIterator { v: self, index: 0 } + } +} + +impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.index = self.index + 1; + self.v.get(self.index - 1) + } +} + +cxxbridge_macro::vector_builtin!(u8); +cxxbridge_macro::vector_builtin!(u16); +cxxbridge_macro::vector_builtin!(u32); +cxxbridge_macro::vector_builtin!(u64); +cxxbridge_macro::vector_builtin!(usize); +cxxbridge_macro::vector_builtin!(i8); +cxxbridge_macro::vector_builtin!(i16); +cxxbridge_macro::vector_builtin!(i32); +cxxbridge_macro::vector_builtin!(i64); +cxxbridge_macro::vector_builtin!(isize); +cxxbridge_macro::vector_builtin!(f32); +cxxbridge_macro::vector_builtin!(f64); diff --git a/syntax/atom.rs b/syntax/atom.rs index eeea831..c68b3fe 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -42,6 +42,43 @@ impl Atom { _ => None, } } + + pub fn to_cxx(&self) -> &'static str { + use self::Atom::*; + match self { + Bool => "bool", + U8 => "uint8_t", + U16 => "uint16_t", + U32 => "uint32_t", + U64 => "uint64_t", + Usize => "size_t", + I8 => "int8_t", + I16 => "int16_t", + I32 => "int32_t", + I64 => "int64_t", + Isize => "::rust::isize", + F32 => "float", + F64 => "double", + CxxString => "::std::string", + RustString => "::rust::String", + } + } + + pub fn is_valid_vector_target(&self) -> bool { + use self::Atom::*; + *self == U8 + || *self == U16 + || *self == U32 + || *self == U64 + || *self == Usize + || *self == I8 + || *self == I16 + || *self == I32 + || *self == I64 + || *self == Isize + || *self == F32 + || *self == F64 + } } impl PartialEq for Ident { diff --git a/syntax/check.rs b/syntax/check.rs index 5e8cc09..387f9a6 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,7 +29,9 @@ fn do_typecheck(cx: &mut Check) { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), + Type::RustVec(ptr) => check_type_vec(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), + Type::Vector(ptr) => check_type_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Slice(ty) => check_type_slice(cx, ty), _ => {} @@ -87,6 +89,21 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported target type of Box"); } +fn check_type_vec(cx: &mut Check, ptr: &Ty1) { + // Vec can contain either user-defined type or u8 + if let Type::Ident(ident) = &ptr.inner { + if Atom::from(ident).map(|a| a.is_valid_vector_target()) == Some(true) { + return; + } else if cx.types.cxx.contains(ident) { + cx.error(ptr, error::VEC_CXX_TYPE.msg); + } else { + return; + } + } + + cx.error(ptr, "unsupported target 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) { @@ -97,11 +114,31 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(CxxString) => return, _ => {} } + } else if let Type::Vector(_) = &ptr.inner { + return; } cx.error(ptr, "unsupported unique_ptr target type"); } +fn check_type_vector(cx: &mut Check, ptr: &Ty1) { + if let Type::Ident(ident) = &ptr.inner { + if cx.types.rust.contains(ident) { + cx.error(ptr, "vector of a Rust type is not supported yet"); + } + + match Atom::from(ident) { + None => return, + Some(atom) => { + if atom.is_valid_vector_target() { + return; + } + } + } + } + cx.error(ptr, "unsupported vector target type"); +} + fn check_type_ref(cx: &mut Check, ty: &Ref) { if ty.lifetime.is_some() { cx.error(ty, "references with explicit lifetimes are not supported"); @@ -310,9 +347,11 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } } Type::RustBox(_) => "Box".to_owned(), + Type::RustVec(_) => "Vec".to_owned(), Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), + Type::Vector(_) => "vector".to_owned(), Type::Slice(_) => "slice".to_owned(), Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), diff --git a/syntax/error.rs b/syntax/error.rs index f52d651..103a54f 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -15,6 +15,7 @@ impl Display for Error { pub static ERRORS: &[Error] = &[ BOX_CXX_TYPE, + VEC_CXX_TYPE, CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, @@ -29,6 +30,12 @@ pub static BOX_CXX_TYPE: Error = Error { note: Some("hint: use UniquePtr<>"), }; +pub static VEC_CXX_TYPE: Error = Error { + msg: "Vec of a C++ type is not supported yet", + label: None, + note: Some("hint: use UniquePtr<>"), +}; + pub static CXXBRIDGE_RESERVED: Error = Error { msg: "identifiers starting with cxxbridge are reserved", label: Some("reserved identifier"), diff --git a/syntax/impls.rs b/syntax/impls.rs index a2ce05b..8e94f06 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -26,6 +26,8 @@ impl Hash for Type { Type::UniquePtr(t) => t.hash(state), Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), + Type::RustVec(t) => t.hash(state), + Type::Vector(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::Slice(t) => t.hash(state), Type::SliceRefU8(t) => t.hash(state), @@ -44,6 +46,8 @@ impl PartialEq for Type { (Type::UniquePtr(lhs), Type::UniquePtr(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::Vector(lhs), Type::Vector(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, diff --git a/syntax/mangled.rs b/syntax/mangled.rs new file mode 100644 index 0000000..56e8b73 --- /dev/null +++ b/syntax/mangled.rs @@ -0,0 +1,30 @@ +use crate::syntax::{Atom, Type}; + +pub trait ToMangled { + fn to_mangled(&self, namespace: &Vec) -> String; +} + +impl ToMangled for Type { + fn to_mangled(&self, namespace: &Vec) -> String { + match self { + Type::Ident(ident) => { + let mut instance = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + instance += name; + instance += "$"; + } + } + instance += &ident.to_string(); + instance + } + Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)), + Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)), + Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)), + Type::Vector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), + _ => unimplemented!(), + } + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index ab5cecc..4e0b908 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -8,11 +8,13 @@ pub mod error; pub mod ident; mod impls; pub mod mangle; +pub mod mangled; pub mod namespace; mod parse; pub mod set; pub mod symbol; mod tokens; +pub mod typename; pub mod types; use self::parse::kw; @@ -86,9 +88,11 @@ pub struct Receiver { pub enum Type { Ident(Ident), RustBox(Box), + RustVec(Box), UniquePtr(Box), Ref(Box), Str(Box), + Vector(Box), Fn(Box), Void(Span), Slice(Box), diff --git a/syntax/namespace.rs b/syntax/namespace.rs index d26bb9e..a4e972b 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -11,7 +11,7 @@ mod kw { #[derive(Clone)] pub struct Namespace { - segments: Vec, + pub segments: Vec, } impl Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index 7d92b1b..f5c598d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,6 +300,16 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Vector" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::Vector(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; @@ -310,6 +320,16 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } + } else if ident == "Vec" && generic.args.len() == 1 { + if let GenericArgument::Type(arg) = &generic.args[0] { + let inner = parse_type(arg)?; + return Ok(Type::RustVec(Box::new(Ty1 { + name: ident, + langle: generic.lt_token, + inner, + rangle: generic.gt_token, + }))); + } } } PathArguments::Parenthesized(_) => {} diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 26bb3d1..3d67a0a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -14,7 +14,9 @@ impl ToTokens for Type { } ident.to_tokens(tokens); } - Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens), + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + ty.to_tokens(tokens) + } Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), Type::Slice(s) => s.to_tokens(tokens), Type::Fn(f) => f.to_tokens(tokens), @@ -33,7 +35,8 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { - if self.name == "UniquePtr" { + // Do not add cxx namespace to Vector since we're defining it in the user crate + if self.name == "UniquePtr" || self.name == "RustVec" { let span = self.name.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } diff --git a/syntax/typename.rs b/syntax/typename.rs new file mode 100644 index 0000000..883e1fb --- /dev/null +++ b/syntax/typename.rs @@ -0,0 +1,36 @@ +use crate::syntax::{Atom, Type}; + +pub trait ToTypename { + fn to_typename(&self, namespace: &Vec) -> String; +} + +impl ToTypename for Type { + fn to_typename(&self, namespace: &Vec) -> String { + match self { + Type::Ident(ident) => { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + inner += name; + inner += "::"; + } + } + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + inner + } + Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)), + Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)), + Type::UniquePtr(ptr) => { + format!("std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) + } + Type::Vector(ptr) => format!("std::vector<{}>", ptr.inner.to_typename(namespace)), + _ => unimplemented!(), + } + } +} diff --git a/syntax/types.rs b/syntax/types.rs index 6f3af09..7bce154 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -24,7 +24,9 @@ impl<'a> Types<'a> { all.insert(ty); match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} - Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner), + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + visit(all, &ty.inner) + } Type::Ref(r) => visit(all, &r.inner), Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index efdd1fa..da0040a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -27,6 +27,9 @@ pub mod ffi { fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; + fn c_return_unique_ptr_vector_u8() -> UniquePtr>; + fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -38,6 +41,11 @@ pub mod ffi { fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); + fn c_take_unique_ptr_vector_u8(s: UniquePtr>); + fn c_take_unique_ptr_vector_f64(s: UniquePtr>); + fn c_take_unique_ptr_vector_shared(s: UniquePtr>); + fn c_take_vec_u8(v: &Vec); + fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 619485a..a8d7018 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,6 +1,7 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs.h" #include +#include #include extern "C" void cxx_test_suite_set_correct() noexcept; @@ -57,6 +58,32 @@ std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); } +std::unique_ptr> c_return_unique_ptr_vector_u8() { + auto retval = + std::unique_ptr>(new std::vector()); + retval->push_back(86); + retval->push_back(75); + retval->push_back(30); + retval->push_back(9); + return retval; +} + +std::unique_ptr> c_return_unique_ptr_vector_f64() { + auto retval = std::unique_ptr>(new std::vector()); + retval->push_back(86.0); + retval->push_back(75.0); + retval->push_back(30.0); + retval->push_back(9.5); + return retval; +} + +std::unique_ptr> c_return_unique_ptr_vector_shared() { + auto retval = std::unique_ptr>(new std::vector()); + retval->push_back(Shared{1010}); + retval->push_back(Shared{1011}); + return retval; +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -118,6 +145,43 @@ void c_take_unique_ptr_string(std::unique_ptr s) { } } +void c_take_unique_ptr_vector_u8(std::unique_ptr> v) { + if (v->size() == 4) { + cxx_test_suite_set_correct(); + } +} + +void c_take_unique_ptr_vector_f64(std::unique_ptr> v) { + if (v->size() == 4) { + cxx_test_suite_set_correct(); + } +} + +void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { + if (v->size() == 2) { + cxx_test_suite_set_correct(); + } +} + +void c_take_vec_u8(const ::rust::Vec &v) { + auto cv = static_cast>(v); + uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); + if (sum == 200) { + cxx_test_suite_set_correct(); + } +} + +void c_take_vec_shared(const ::rust::Vec &v) { + auto cv = static_cast>(v); + uint32_t sum = 0; + for (auto i : cv) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + void c_take_callback(rust::Fn callback) { callback("2020"); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index ed6d541..876e3c5 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -29,6 +29,9 @@ rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); +std::unique_ptr> c_return_unique_ptr_vector_u8(); +std::unique_ptr> c_return_unique_ptr_vector_f64(); +std::unique_ptr> c_return_unique_ptr_vector_shared(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -40,6 +43,11 @@ void c_take_str(rust::Str s); void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); +void c_take_unique_ptr_vector_u8(std::unique_ptr> v); +void c_take_unique_ptr_vector_f64(std::unique_ptr> v); +void c_take_unique_ptr_vector_shared(std::unique_ptr> v); +void c_take_vec_u8(const ::rust::Vec &v); +void c_take_vec_shared(const ::rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); diff --git a/tests/test.rs b/tests/test.rs index 1cd85df..b33c675 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -32,6 +32,45 @@ fn test_c_return() { assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); + assert_eq!( + 4, + ffi::c_return_unique_ptr_vector_u8() + .as_ref() + .unwrap() + .size() + ); + assert_eq!( + 200_u8, + ffi::c_return_unique_ptr_vector_u8() + .as_ref() + .unwrap() + .into_iter() + .sum() + ); + assert_eq!( + 200.5_f64, + ffi::c_return_unique_ptr_vector_f64() + .as_ref() + .unwrap() + .into_iter() + .sum() + ); + assert_eq!( + 2, + ffi::c_return_unique_ptr_vector_shared() + .as_ref() + .unwrap() + .size() + ); + assert_eq!( + 2021_usize, + ffi::c_return_unique_ptr_vector_shared() + .as_ref() + .unwrap() + .into_iter() + .map(|o| o.z) + .sum() + ); } #[test] @@ -65,6 +104,20 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string() )); + check!(ffi::c_take_unique_ptr_vector_u8( + ffi::c_return_unique_ptr_vector_u8() + )); + check!(ffi::c_take_unique_ptr_vector_f64( + ffi::c_return_unique_ptr_vector_f64() + )); + check!(ffi::c_take_unique_ptr_vector_shared( + ffi::c_return_unique_ptr_vector_shared() + )); + check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); + check!(ffi::c_take_vec_shared(&vec![ + ffi::Shared { z: 1010 }, + ffi::Shared { z: 1011 } + ])); } #[test] From 8b7f8993479a981d5a4b5129e364c28cee5d5fc5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 359/2232] Place vector include in sorted order --- diff --git a/gen/include.rs b/gen/include.rs index 077d03e..137f52b 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -36,9 +36,9 @@ pub struct Includes { pub exception: bool, pub memory: bool, pub string: bool, - pub vector: bool, pub type_traits: bool, pub utility: bool, + pub vector: bool, pub base_tsd: bool, } From 122905eb229ce5f64f3933a9ebe4b63aef19f962 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 360/2232] Emit include if generated code requires it --- diff --git a/gen/include.rs b/gen/include.rs index 137f52b..129a8e6 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -90,6 +90,9 @@ impl Display for Includes { if self.utility { writeln!(f, "#include ")?; } + if self.vector { + writeln!(f, "#include ")?; + } if self.base_tsd { writeln!(f, "#if defined(_WIN32)")?; writeln!(f, "#include ")?; From 76c13bf9da2f4a43ba51f0720a739b9b43021b6a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 361/2232] Remove unused type_traits import when generating Rust Vec --- diff --git a/gen/write.rs b/gen/write.rs index 8a60f17..270b26e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -147,7 +147,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_box = true; } Type::RustVec(_) => { - out.include.type_traits = true; needs_rust_vec = true; } Type::Str(_) => { From 4f6dd4e631173453859ba318a3afb69779158555 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 362/2232] Simplify vector tests using UniquePtr deref --- diff --git a/tests/test.rs b/tests/test.rs index b33c675..f394c7a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -32,44 +32,22 @@ fn test_c_return() { assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); - assert_eq!( - 4, - ffi::c_return_unique_ptr_vector_u8() - .as_ref() - .unwrap() - .size() - ); + assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().size()); assert_eq!( 200_u8, - ffi::c_return_unique_ptr_vector_u8() - .as_ref() - .unwrap() - .into_iter() - .sum() + ffi::c_return_unique_ptr_vector_u8().into_iter().sum(), ); assert_eq!( 200.5_f64, - ffi::c_return_unique_ptr_vector_f64() - .as_ref() - .unwrap() - .into_iter() - .sum() - ); - assert_eq!( - 2, - ffi::c_return_unique_ptr_vector_shared() - .as_ref() - .unwrap() - .size() + ffi::c_return_unique_ptr_vector_f64().into_iter().sum(), ); + assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().size()); assert_eq!( 2021_usize, ffi::c_return_unique_ptr_vector_shared() - .as_ref() - .unwrap() .into_iter() .map(|o| o.z) - .sum() + .sum(), ); } From d1413040aed617ec8653942cd925d909746f0ee3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 363/2232] Remove absolute paths in non-generated code --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 4940834..efa1d1e 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -24,7 +24,7 @@ std::unique_ptr> do_thing(SharedThing state) { return vec; } -JsonBlob get_jb(const ::rust::Vec &vec) { +JsonBlob get_jb(const rust::Vec &vec) { JsonBlob retval; std::cout << "incoming vec length is " << vec.size() << "\n"; diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 2c9f1c0..9a90716 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -20,7 +20,7 @@ struct JsonBlob; std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); std::unique_ptr> do_thing(SharedThing state); -JsonBlob get_jb(const ::rust::Vec &vec); +JsonBlob get_jb(const rust::Vec &vec); } // namespace example } // namespace org diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index a8d7018..1dd3b73 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -163,7 +163,7 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { } } -void c_take_vec_u8(const ::rust::Vec &v) { +void c_take_vec_u8(const rust::Vec &v) { auto cv = static_cast>(v); uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); if (sum == 200) { @@ -171,7 +171,7 @@ void c_take_vec_u8(const ::rust::Vec &v) { } } -void c_take_vec_shared(const ::rust::Vec &v) { +void c_take_vec_shared(const rust::Vec &v) { auto cv = static_cast>(v); uint32_t sum = 0; for (auto i : cv) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 876e3c5..e1e163d 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -46,8 +46,8 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); -void c_take_vec_u8(const ::rust::Vec &v); -void c_take_vec_shared(const ::rust::Vec &v); +void c_take_vec_u8(const rust::Vec &v); +void c_take_vec_shared(const rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); From 91d1bb9b7dec795f24a25a31f675d721c8e069bb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 364/2232] Use absolute paths consistently in generated code --- diff --git a/gen/write.rs b/gen/write.rs index 270b26e..cb61978 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -990,7 +990,7 @@ fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { ); writeln!( out, - "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const std::vector<{}> &vector) noexcept;", + "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const ::std::vector<{}> &vector) noexcept;", instance, inner, inner ); writeln!( @@ -1043,12 +1043,12 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!(out, "template <>"); writeln!( out, - "Vec<{}>::operator std::vector<{}>() const noexcept {{", + "Vec<{}>::operator ::std::vector<{}>() const noexcept {{", inner, inner ); writeln!( out, - " std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", + " ::std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", inner, instance, ); writeln!(out, "}}"); @@ -1149,7 +1149,7 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); writeln!( out, - "size_t cxxbridge02$std$vector${}$length(const std::vector<{}> &s) noexcept {{", + "size_t cxxbridge02$std$vector${}$length(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); @@ -1157,7 +1157,7 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { writeln!( out, - "void cxxbridge02$std$vector${}$push_back(std::vector<{}> &s, const {} &item) noexcept {{", + "void cxxbridge02$std$vector${}$push_back(::std::vector<{}> &s, const {} &item) noexcept {{", instance, inner, inner ); writeln!(out, " s.push_back(item);"); @@ -1165,7 +1165,7 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { writeln!( out, - "const {} *cxxbridge02$std$vector${}$get_unchecked(const std::vector<{}> &s, size_t pos) noexcept {{", + "const {} *cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); writeln!(out, " return &s[pos];"); diff --git a/syntax/typename.rs b/syntax/typename.rs index 883e1fb..7ad1be2 100644 --- a/syntax/typename.rs +++ b/syntax/typename.rs @@ -27,9 +27,9 @@ impl ToTypename for Type { Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)), Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)), Type::UniquePtr(ptr) => { - format!("std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) + format!("::std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) } - Type::Vector(ptr) => format!("std::vector<{}>", ptr.inner.to_typename(namespace)), + Type::Vector(ptr) => format!("::std::vector<{}>", ptr.inner.to_typename(namespace)), _ => unimplemented!(), } } From 4fcfa945c65851149cd9ed9db0ccd49d4e154e92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:07 +0000 Subject: [PATCH 365/2232] Wrap implementation of vector type conversion --- diff --git a/gen/write.rs b/gen/write.rs index cb61978..e89cf0b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1046,11 +1046,14 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { "Vec<{}>::operator ::std::vector<{}>() const noexcept {{", inner, inner ); + writeln!(out, " ::std::vector<{}> v;", inner); + writeln!(out, " v.reserve(this->size());"); writeln!( out, - " ::std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", - inner, instance, + " cxxbridge02$rust_vec${}$vector_from(this, v);", + instance, ); + writeln!(out, " return v;"); writeln!(out, "}}"); } From 85db5a01b2fc9f6513ee17b93f2f6d2fbb1d5285 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 366/2232] Touch up PR 67 --- diff --git a/gen/write.rs b/gen/write.rs index e89cf0b..911a3d2 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1031,7 +1031,7 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!( out, " return cxxbridge02$rust_vec${}$drop(this);", - instance + instance, ); writeln!(out, "}}"); @@ -1044,7 +1044,7 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!( out, "Vec<{}>::operator ::std::vector<{}>() const noexcept {{", - inner, inner + inner, inner, ); writeln!(out, " ::std::vector<{}> v;", inner); writeln!(out, " v.reserve(this->size());"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 95a6f1a..1d40419 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -602,6 +602,7 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) { this.as_ref().unwrap().into_vector(vector.as_mut().unwrap()); } + #[doc(hidden)] #[export_name = #link_len] unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { this.as_ref().unwrap().len() diff --git a/src/vector.rs b/src/vector.rs index 805bdbb..1436165 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -71,6 +71,7 @@ impl<'a, T: VectorTarget> IntoIterator for &'a RealVector { impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { type Item = &'a T; + fn next(&mut self) -> Option { self.index = self.index + 1; self.v.get(self.index - 1) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 1dd3b73..46f04be 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -59,29 +59,28 @@ std::unique_ptr c_return_unique_ptr_string() { } std::unique_ptr> c_return_unique_ptr_vector_u8() { - auto retval = - std::unique_ptr>(new std::vector()); - retval->push_back(86); - retval->push_back(75); - retval->push_back(30); - retval->push_back(9); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(86); + vec->push_back(75); + vec->push_back(30); + vec->push_back(9); + return vec; } std::unique_ptr> c_return_unique_ptr_vector_f64() { - auto retval = std::unique_ptr>(new std::vector()); - retval->push_back(86.0); - retval->push_back(75.0); - retval->push_back(30.0); - retval->push_back(9.5); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(86.0); + vec->push_back(75.0); + vec->push_back(30.0); + vec->push_back(9.5); + return vec; } std::unique_ptr> c_return_unique_ptr_vector_shared() { - auto retval = std::unique_ptr>(new std::vector()); - retval->push_back(Shared{1010}); - retval->push_back(Shared{1011}); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(Shared{1010}); + vec->push_back(Shared{1011}); + return vec; } void c_take_primitive(size_t n) { From 4f644704123d6614a627a91aae600dd4c6721af3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 367/2232] Remove vector manipulation from demo project The vector support needs to be covered by our test suite, but it doesn't need to be featured to this extent in our intro code. --- diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index efa1d1e..cd447ea 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -15,35 +15,7 @@ std::unique_ptr make_demo(rust::Str appname) { const std::string &get_name(const ThingC &thing) { return thing.appname; } -std::unique_ptr> do_thing(SharedThing state) { - print_r(*state.y); - auto vec = std::unique_ptr>(new std::vector()); - for (uint8_t i = 0; i < 10; i++) { - vec->push_back(i * i); - } - return vec; -} - -JsonBlob get_jb(const rust::Vec &vec) { - JsonBlob retval; - - std::cout << "incoming vec length is " << vec.size() << "\n"; - auto vec_copy = static_cast>(vec); - std::cout << "vec_copy length is " << vec_copy.size() << "\n"; - std::cout << "vec_copy[0] is " << (int)vec_copy[0] << "\n"; - - auto blob = std::unique_ptr>(new std::vector()); - for (uint8_t i = 0; i < 10; i++) { - blob->push_back(i * 2); - } - - auto json = std::unique_ptr(new std::string("{\"demo\": 23}")); - - retval.json = std::move(json); - retval.blob = std::move(blob); - - return retval; -} +void do_thing(SharedThing state) { print_r(*state.y); } } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 9a90716..fafc474 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -15,12 +15,10 @@ public: }; struct SharedThing; -struct JsonBlob; std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); -std::unique_ptr> do_thing(SharedThing state); -JsonBlob get_jb(const rust::Vec &vec); +void do_thing(SharedThing state); } // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 8bf9926..66dfc79 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -6,19 +6,13 @@ mod ffi { x: UniquePtr, } - struct JsonBlob { - json: UniquePtr, - blob: UniquePtr>, - } - extern "C" { include!("demo-cxx/demo.h"); type ThingC; fn make_demo(appname: &str) -> UniquePtr; fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing) -> UniquePtr>; - fn get_jb(v: &Vec) -> JsonBlob; + fn do_thing(state: SharedThing); } extern "Rust" { @@ -37,24 +31,9 @@ fn main() { let x = ffi::make_demo("demo of cxx::bridge"); println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - let vec = ffi::do_thing(ffi::SharedThing { + ffi::do_thing(ffi::SharedThing { z: 222, y: Box::new(ThingR(333)), x, }); - - println!("vec length = {}", vec.as_ref().unwrap().size()); - for (i, v) in vec.as_ref().unwrap().into_iter().enumerate() { - println!("vec[{}] = {}", i, v); - } - - let mut rv: Vec = Vec::new(); - for _ in 0..1000 { - rv.push(33); - } - let jb = ffi::get_jb(&rv); - println!("json: {}", jb.json.as_ref().unwrap()); - for (i, v) in jb.blob.as_ref().unwrap().into_iter().enumerate() { - println!("jb.blob[{}] = {}", i, v); - } } From a9a7ed18e688456c38ad7bc9362bfe8e72a0f1c3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 368/2232] Remove rust::Vec::operator std::vector --- diff --git a/gen/write.rs b/gen/write.rs index 911a3d2..c1aa2f7 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -990,11 +990,6 @@ fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { ); writeln!( out, - "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const ::std::vector<{}> &vector) noexcept;", - instance, inner, inner - ); - writeln!( - out, "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); @@ -1039,22 +1034,6 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!( - out, - "Vec<{}>::operator ::std::vector<{}>() const noexcept {{", - inner, inner, - ); - writeln!(out, " ::std::vector<{}> v;", inner); - writeln!(out, " v.reserve(this->size());"); - writeln!( - out, - " cxxbridge02$rust_vec${}$vector_from(this, v);", - instance, - ); - writeln!(out, " return v;"); - writeln!(out, "}}"); } fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { diff --git a/include/cxx.h b/include/cxx.h index 8f03cbb..c62faad 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -90,7 +90,6 @@ template class Vec final { public: size_t size() const noexcept; - explicit operator std::vector() const noexcept; private: Vec() noexcept; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1d40419..f29eeeb 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -583,12 +583,10 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre let mangled = ty.to_mangled(&namespace.segments) + "$"; let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); let link_drop = format!("{}drop", link_prefix); - let link_vector_from = format!("{}vector_from", link_prefix); let link_len = format!("{}len", link_prefix); let local_prefix = format_ident!("{}__vec_", ident); let local_drop = format_ident!("{}drop", local_prefix); - let local_vector_from = format_ident!("{}vector_from", local_prefix); let local_len = format_ident!("{}len", local_prefix); let span = ty.span(); @@ -598,10 +596,6 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { std::ptr::drop_in_place(this); } - #[export_name = #link_vector_from] - unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) { - this.as_ref().unwrap().into_vector(vector.as_mut().unwrap()); - } #[doc(hidden)] #[export_name = #link_len] unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { diff --git a/src/rust_vec.rs b/src/rust_vec.rs index a28570d..2c8441b 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,4 +1,3 @@ -use crate::vector::RealVector; use crate::vector::VectorTarget; #[repr(C)] @@ -30,10 +29,4 @@ impl> RustVec { pub fn len(&self) -> usize { self.repr.len() } - - pub fn into_vector(&self, vec: &mut RealVector) { - for item in &self.repr { - vec.push_back(item); - } - } } From 4c64afbcbee153b1d9609488b442df0606957ae1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 369/2232] Fix missing absolute path to drop_in_place It's possible for `std` to mean something different from `::std` if the user's ffi mod contains a type named `std`. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index f29eeeb..e882087 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -594,7 +594,7 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre #[doc(hidden)] #[export_name = #link_drop] unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { - std::ptr::drop_in_place(this); + ::std::ptr::drop_in_place(this); } #[doc(hidden)] #[export_name = #link_len] From 2cef5df835fd8443ad9fad9998484d48621a1b47 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 370/2232] Remove panicking codepath from vec$len --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e882087..89d15a9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -599,7 +599,7 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre #[doc(hidden)] #[export_name = #link_len] unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { - this.as_ref().unwrap().len() + (*this).len() } } } From fac8b25a2590ffc1fca971d71ff037288af422f2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 371/2232] Update RustVec::from_ref to match RustString::from_ref --- diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 2c8441b..b4eed3b 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -11,7 +11,7 @@ impl> RustVec { } pub fn from_ref(v: &Vec) -> &Self { - unsafe { std::mem::transmute::<&Vec, &RustVec>(v) } + unsafe { &*(v as *const Vec as *const RustVec) } } pub fn into_vec(self) -> Vec { From 6c6b7e0cec18c580737b935b3f02cc53751d68b6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 372/2232] Remove private Vec ffi wrapper from public API --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 89d15a9..5554d28 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -250,12 +250,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), - Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)), + Type::RustVec(_) => quote!(::cxx::private::RustVec::from(#var)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) } - Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)), + Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)), _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -593,12 +593,12 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre quote_spanned! {span=> #[doc(hidden)] #[export_name = #link_drop] - unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { + unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#inner>) { ::std::ptr::drop_in_place(this); } #[doc(hidden)] #[export_name = #link_len] - unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { + unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#inner>) -> usize { (*this).len() } } @@ -782,12 +782,12 @@ fn expand_extern_type(ty: &Type) -> TokenStream { let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) } - Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>), + Type::RustVec(ty) => quote!(::cxx::private::RustVec<#ty>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), Type::RustVec(ty) => { let inner = expand_extern_type(&ty.inner); - quote!(&::cxx::RustVec<#inner>) + quote!(&::cxx::private::RustVec<#inner>) } _ => quote!(#ty), }, diff --git a/src/lib.rs b/src/lib.rs index e23be69..a8ce904 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,7 +378,6 @@ mod vector; pub use crate::cxx_string::CxxString; pub use crate::exception::Exception; -pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtr; pub use crate::vector::RealVector; pub use crate::vector::VectorIntoIterator; @@ -393,6 +392,7 @@ pub mod private { pub use crate::rust_sliceu8::RustSliceU8; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; + pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; pub use crate::vector::VectorTarget; From e90be1da4dc67406db3d2aff183a37ec09a09f83 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:08 +0000 Subject: [PATCH 373/2232] Rename std::vector binding to CxxVector --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 5554d28..782da05 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -26,7 +26,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { // "Header" to define newtypes locally so we can implement // traits on them. expanded.extend(quote! { - pub struct Vector(pub ::cxx::RealVector); + pub struct Vector(pub ::cxx::CxxVector); impl> Vector { pub fn size(&self) -> usize { self.0.size() @@ -46,7 +46,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } impl<'a, T: cxx::private::VectorTarget> IntoIterator for &'a Vector { type Item = &'a T; - type IntoIter = <&'a ::cxx::RealVector as IntoIterator>::IntoIter; + type IntoIter = <&'a ::cxx::CxxVector as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() @@ -112,7 +112,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::Vector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - // Generate code for Vector if T is not an atom + // Generate code for CxxVector if T is not an atom // Code for atoms is already generated expanded.extend(expand_vector(namespace, &ptr.inner)); } @@ -688,29 +688,29 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { quote! { impl ::cxx::private::VectorTarget<#inner> for #inner { - fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner { + fn get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner; + fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; } unsafe { __get_unchecked(v, pos) } } - fn vector_length(v: &::cxx::RealVector<#inner>) -> usize { + fn vector_length(v: &::cxx::CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] - fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize; + fn __vector_length(_: &::cxx::CxxVector<#inner>) -> usize; } __vector_length(v) } } - fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) { + fn push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { unsafe { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize; + fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner) -> usize; } __push_back(v, item); } @@ -731,29 +731,29 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { quote! { impl VectorTarget<#inner> for #inner { - fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner { + fn get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner; + fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; } unsafe { __get_unchecked(v, pos) } } - fn vector_length(v: &RealVector<#inner>) -> usize { + fn vector_length(v: &CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] - fn __vector_length(_: &RealVector<#inner>) -> usize; + fn __vector_length(_: &CxxVector<#inner>) -> usize; } __vector_length(v) } } - fn push_back(v: &RealVector<#inner>, item: &#inner) { + fn push_back(v: &CxxVector<#inner>, item: &#inner) { unsafe { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize; + fn __push_back(_: &CxxVector<#inner>, _: &#inner) -> usize; } __push_back(v, item); } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs new file mode 100644 index 0000000..feb2e29 --- /dev/null +++ b/src/cxx_vector.rs @@ -0,0 +1,92 @@ +pub trait VectorTarget { + fn get_unchecked(v: &CxxVector, pos: usize) -> &T + where + Self: Sized; + fn vector_length(v: &CxxVector) -> usize + where + Self: Sized; + fn push_back(v: &CxxVector, item: &T) + where + Self: Sized; +} + +/// Binding to C++ `std::vector`. +/// +/// # Invariants +/// +/// As an invariant of this API and the static analysis of the cxx::bridge +/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector +/// requires a move constructor and may hold internal pointers, which is not +/// compatible with Rust's move behavior. Instead in Rust code we will only ever +/// look at a Vector through a reference or smart pointer, as in `&Vector` +/// or `UniquePtr`. +#[repr(C)] +pub struct CxxVector { + _private: [T; 0], +} + +impl> CxxVector { + /// Returns the length of the vector in bytes. + pub fn size(&self) -> usize { + T::vector_length(self) + } + + pub fn get_unchecked(&self, pos: usize) -> &T { + T::get_unchecked(self, pos) + } + + /// Returns true if `self` has a length of zero bytes. + pub fn is_empty(&self) -> bool { + self.size() == 0 + } + + pub fn get(&self, pos: usize) -> Option<&T> { + if pos < self.size() { + Some(self.get_unchecked(pos)) + } else { + None + } + } + + pub fn push_back(&mut self, item: &T) { + T::push_back(self, item); + } +} + +unsafe impl Send for CxxVector where T: Send + VectorTarget {} + +pub struct VectorIntoIterator<'a, T> { + v: &'a CxxVector, + index: usize, +} + +impl<'a, T: VectorTarget> IntoIterator for &'a CxxVector { + type Item = &'a T; + type IntoIter = VectorIntoIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + VectorIntoIterator { v: self, index: 0 } + } +} + +impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.index = self.index + 1; + self.v.get(self.index - 1) + } +} + +cxxbridge_macro::vector_builtin!(u8); +cxxbridge_macro::vector_builtin!(u16); +cxxbridge_macro::vector_builtin!(u32); +cxxbridge_macro::vector_builtin!(u64); +cxxbridge_macro::vector_builtin!(usize); +cxxbridge_macro::vector_builtin!(i8); +cxxbridge_macro::vector_builtin!(i16); +cxxbridge_macro::vector_builtin!(i32); +cxxbridge_macro::vector_builtin!(i64); +cxxbridge_macro::vector_builtin!(isize); +cxxbridge_macro::vector_builtin!(f32); +cxxbridge_macro::vector_builtin!(f64); diff --git a/src/lib.rs b/src/lib.rs index a8ce904..a25f83d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,6 +360,7 @@ extern crate link_cplusplus; mod assert; mod cxx_string; +mod cxx_vector; mod error; mod exception; mod function; @@ -374,18 +375,17 @@ mod rust_vec; mod syntax; mod unique_ptr; mod unwind; -mod vector; pub use crate::cxx_string::CxxString; +pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; pub use crate::unique_ptr::UniquePtr; -pub use crate::vector::RealVector; -pub use crate::vector::VectorIntoIterator; pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::cxx_vector::VectorTarget; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; @@ -395,7 +395,6 @@ pub mod private { pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; - pub use crate::vector::VectorTarget; } use crate::error::Result; diff --git a/src/rust_vec.rs b/src/rust_vec.rs index b4eed3b..671d8f7 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,4 +1,4 @@ -use crate::vector::VectorTarget; +use crate::cxx_vector::VectorTarget; #[repr(C)] pub struct RustVec> { diff --git a/src/vector.rs b/src/vector.rs deleted file mode 100644 index 1436165..0000000 --- a/src/vector.rs +++ /dev/null @@ -1,92 +0,0 @@ -pub trait VectorTarget { - fn get_unchecked(v: &RealVector, pos: usize) -> &T - where - Self: Sized; - fn vector_length(v: &RealVector) -> usize - where - Self: Sized; - fn push_back(v: &RealVector, item: &T) - where - Self: Sized; -} - -/// Binding to C++ `std::vector`. -/// -/// # Invariants -/// -/// As an invariant of this API and the static analysis of the cxx::bridge -/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector -/// requires a move constructor and may hold internal pointers, which is not -/// compatible with Rust's move behavior. Instead in Rust code we will only ever -/// look at a Vector through a reference or smart pointer, as in `&Vector` -/// or `UniquePtr`. -#[repr(C)] -pub struct RealVector { - _private: [T; 0], -} - -impl> RealVector { - /// Returns the length of the vector in bytes. - pub fn size(&self) -> usize { - T::vector_length(self) - } - - pub fn get_unchecked(&self, pos: usize) -> &T { - T::get_unchecked(self, pos) - } - - /// Returns true if `self` has a length of zero bytes. - pub fn is_empty(&self) -> bool { - self.size() == 0 - } - - pub fn get(&self, pos: usize) -> Option<&T> { - if pos < self.size() { - Some(self.get_unchecked(pos)) - } else { - None - } - } - - pub fn push_back(&mut self, item: &T) { - T::push_back(self, item); - } -} - -unsafe impl Send for RealVector where T: Send + VectorTarget {} - -pub struct VectorIntoIterator<'a, T> { - v: &'a RealVector, - index: usize, -} - -impl<'a, T: VectorTarget> IntoIterator for &'a RealVector { - type Item = &'a T; - type IntoIter = VectorIntoIterator<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - VectorIntoIterator { v: self, index: 0 } - } -} - -impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option { - self.index = self.index + 1; - self.v.get(self.index - 1) - } -} - -cxxbridge_macro::vector_builtin!(u8); -cxxbridge_macro::vector_builtin!(u16); -cxxbridge_macro::vector_builtin!(u32); -cxxbridge_macro::vector_builtin!(u64); -cxxbridge_macro::vector_builtin!(usize); -cxxbridge_macro::vector_builtin!(i8); -cxxbridge_macro::vector_builtin!(i16); -cxxbridge_macro::vector_builtin!(i32); -cxxbridge_macro::vector_builtin!(i64); -cxxbridge_macro::vector_builtin!(isize); -cxxbridge_macro::vector_builtin!(f32); -cxxbridge_macro::vector_builtin!(f64); From 4f7e6fa1f55866d131cfc8d685ceb267febac237 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 374/2232] Fix align of CxxVector A vector whose element type is highly aligned need not itself be highly aligned. If C++ passed in a reference to a vector which is less aligned than its element type, and Rust thought vectors were more highly aligned then they really are, then that would have been undefined behavior. --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index feb2e29..6cf927b 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,3 +1,5 @@ +use std::mem; + pub trait VectorTarget { fn get_unchecked(v: &CxxVector, pos: usize) -> &T where @@ -20,7 +22,7 @@ pub trait VectorTarget { /// compatible with Rust's move behavior. Instead in Rust code we will only ever /// look at a Vector through a reference or smart pointer, as in `&Vector` /// or `UniquePtr`. -#[repr(C)] +#[repr(C, packed)] pub struct CxxVector { _private: [T; 0], } @@ -90,3 +92,5 @@ cxxbridge_macro::vector_builtin!(i64); cxxbridge_macro::vector_builtin!(isize); cxxbridge_macro::vector_builtin!(f32); cxxbridge_macro::vector_builtin!(f64); + +const_assert_eq!(1, mem::align_of::>()); From 3a8ae095a3922551f3191844437894217f41d3f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 375/2232] Decouple Vec ffi wrapper from C++ vector elements --- diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 671d8f7..a8a144a 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,11 +1,9 @@ -use crate::cxx_vector::VectorTarget; - #[repr(C)] -pub struct RustVec> { +pub struct RustVec { repr: Vec, } -impl> RustVec { +impl RustVec { pub fn from(v: Vec) -> Self { RustVec { repr: v } } From 9e8ec74f2b1996952b9f51369f341ef6f0cd4a94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 376/2232] Remove unneeded unsafe Send impl for CxxVector This autotrait impl is already inferred. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 782da05..9eb1e56 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -52,7 +52,6 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { self.0.into_iter() } } - unsafe impl Send for Vector where T: Send + cxx::private::VectorTarget {} }); for api in &apis { diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 6cf927b..3e3fd03 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -55,8 +55,6 @@ impl> CxxVector { } } -unsafe impl Send for CxxVector where T: Send + VectorTarget {} - pub struct VectorIntoIterator<'a, T> { v: &'a CxxVector, index: usize, From 5fe936375e5b5ae27ad4f95e0dcdedd23e9e05f3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 377/2232] Update CxxVector invariant documentation --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 3e3fd03..0be971d 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -17,11 +17,9 @@ pub trait VectorTarget { /// # Invariants /// /// As an invariant of this API and the static analysis of the cxx::bridge -/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector -/// requires a move constructor and may hold internal pointers, which is not -/// compatible with Rust's move behavior. Instead in Rust code we will only ever -/// look at a Vector through a reference or smart pointer, as in `&Vector` -/// or `UniquePtr`. +/// macro, in Rust code we can never obtain a `CxxVector` by value. Instead in +/// Rust code we will only ever look at a vector behind a reference or smart +/// pointer, as in `&CxxVector` or `UniquePtr>`. #[repr(C, packed)] pub struct CxxVector { _private: [T; 0], From 67ba0b523191488ab4875065eb90ff010d48c511 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 378/2232] Rename VectorTarget to VectorElement I assume VectorTarget was copied from the naming of UniquePtrTarget, but pointers have a "target" (i.e. Deref) while vectors have an "element". --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9eb1e56..4ecaf07 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -27,7 +27,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { // traits on them. expanded.extend(quote! { pub struct Vector(pub ::cxx::CxxVector); - impl> Vector { + impl> Vector { pub fn size(&self) -> usize { self.0.size() } @@ -44,7 +44,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { self.0.push_back(item) } } - impl<'a, T: cxx::private::VectorTarget> IntoIterator for &'a Vector { + impl<'a, T: cxx::private::VectorElement> IntoIterator for &'a Vector { type Item = &'a T; type IntoIter = <&'a ::cxx::CxxVector as IntoIterator>::IntoIter; @@ -686,7 +686,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - impl ::cxx::private::VectorTarget<#inner> for #inner { + impl ::cxx::private::VectorElement<#inner> for #inner { fn get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] @@ -729,7 +729,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - impl VectorTarget<#inner> for #inner { + impl VectorElement<#inner> for #inner { fn get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 0be971d..d81b4fe 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,6 +1,6 @@ use std::mem; -pub trait VectorTarget { +pub trait VectorElement { fn get_unchecked(v: &CxxVector, pos: usize) -> &T where Self: Sized; @@ -25,7 +25,7 @@ pub struct CxxVector { _private: [T; 0], } -impl> CxxVector { +impl> CxxVector { /// Returns the length of the vector in bytes. pub fn size(&self) -> usize { T::vector_length(self) @@ -58,7 +58,7 @@ pub struct VectorIntoIterator<'a, T> { index: usize, } -impl<'a, T: VectorTarget> IntoIterator for &'a CxxVector { +impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { type Item = &'a T; type IntoIter = VectorIntoIterator<'a, T>; @@ -67,7 +67,7 @@ impl<'a, T: VectorTarget> IntoIterator for &'a CxxVector { } } -impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { +impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { diff --git a/src/lib.rs b/src/lib.rs index a25f83d..1af4d0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -385,7 +385,7 @@ pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { - pub use crate::cxx_vector::VectorTarget; + pub use crate::cxx_vector::VectorElement; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; From b0b83b2477b3675d2b0a951a4c57fb6213b49d78 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 379/2232] Fix missing absolute paths in vector-related Rust codegen --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4ecaf07..d8c47d4 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -27,11 +27,11 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { // traits on them. expanded.extend(quote! { pub struct Vector(pub ::cxx::CxxVector); - impl> Vector { + impl> Vector { pub fn size(&self) -> usize { self.0.size() } - pub fn get(&self, pos: usize) -> Option<&T> { + pub fn get(&self, pos: usize) -> ::std::option::Option<&T> { self.0.get(pos) } pub fn get_unchecked(&self, pos: usize) -> &T { @@ -44,9 +44,9 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { self.0.push_back(item) } } - impl<'a, T: cxx::private::VectorElement> IntoIterator for &'a Vector { + impl<'a, T: ::cxx::private::VectorElement> ::std::iter::IntoIterator for &'a Vector { type Item = &'a T; - type IntoIter = <&'a ::cxx::CxxVector as IntoIterator>::IntoIter; + type IntoIter = <&'a ::cxx::CxxVector as ::std::iter::IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() From 69b7f17bc6e0af7c829d48ad183114886c88dbf3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 380/2232] Remove unused Vector wrapper around CxxVector --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d8c47d4..8b0b6a1 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -23,37 +23,6 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - // "Header" to define newtypes locally so we can implement - // traits on them. - expanded.extend(quote! { - pub struct Vector(pub ::cxx::CxxVector); - impl> Vector { - pub fn size(&self) -> usize { - self.0.size() - } - pub fn get(&self, pos: usize) -> ::std::option::Option<&T> { - self.0.get(pos) - } - pub fn get_unchecked(&self, pos: usize) -> &T { - self.0.get_unchecked(pos) - } - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - pub fn push_back(&mut self, item: &T) { - self.0.push_back(item) - } - } - impl<'a, T: ::cxx::private::VectorElement> ::std::iter::IntoIterator for &'a Vector { - type Item = &'a T; - type IntoIter = <&'a ::cxx::CxxVector as ::std::iter::IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } - } - }); - for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); From 1b34119261e2b5c5be1ee2216758d903b97f8a7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:09 +0000 Subject: [PATCH 381/2232] Hide VectorElement trait from docs --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index d81b4fe..f7185bb 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,17 +1,5 @@ use std::mem; -pub trait VectorElement { - fn get_unchecked(v: &CxxVector, pos: usize) -> &T - where - Self: Sized; - fn vector_length(v: &CxxVector) -> usize - where - Self: Sized; - fn push_back(v: &CxxVector, item: &T) - where - Self: Sized; -} - /// Binding to C++ `std::vector`. /// /// # Invariants @@ -76,6 +64,19 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { } } +#[doc(hidden)] +pub trait VectorElement { + fn get_unchecked(v: &CxxVector, pos: usize) -> &T + where + Self: Sized; + fn vector_length(v: &CxxVector) -> usize + where + Self: Sized; + fn push_back(v: &CxxVector, item: &T) + where + Self: Sized; +} + cxxbridge_macro::vector_builtin!(u8); cxxbridge_macro::vector_builtin!(u16); cxxbridge_macro::vector_builtin!(u32); From 0a63b4cf1053383c82392c1af17d0bace08a0b72 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 382/2232] Hide VectorElement methods from autocomplete --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8b0b6a1..477495a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -656,7 +656,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { quote! { impl ::cxx::private::VectorElement<#inner> for #inner { - fn get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { + fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; @@ -665,7 +665,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { __get_unchecked(v, pos) } } - fn vector_length(v: &::cxx::CxxVector<#inner>) -> usize { + fn __vector_length(v: &::cxx::CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] @@ -674,7 +674,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { __vector_length(v) } } - fn push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { + fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { unsafe { extern "C" { #[link_name = #link_push_back] @@ -699,7 +699,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { quote! { impl VectorElement<#inner> for #inner { - fn get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { + fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; @@ -708,7 +708,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { __get_unchecked(v, pos) } } - fn vector_length(v: &CxxVector<#inner>) -> usize { + fn __vector_length(v: &CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] @@ -717,7 +717,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { __vector_length(v) } } - fn push_back(v: &CxxVector<#inner>, item: &#inner) { + fn __push_back(v: &CxxVector<#inner>, item: &#inner) { unsafe { extern "C" { #[link_name = #link_push_back] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index f7185bb..7db8a45 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -16,11 +16,11 @@ pub struct CxxVector { impl> CxxVector { /// Returns the length of the vector in bytes. pub fn size(&self) -> usize { - T::vector_length(self) + T::__vector_length(self) } pub fn get_unchecked(&self, pos: usize) -> &T { - T::get_unchecked(self, pos) + T::__get_unchecked(self, pos) } /// Returns true if `self` has a length of zero bytes. @@ -37,7 +37,7 @@ impl> CxxVector { } pub fn push_back(&mut self, item: &T) { - T::push_back(self, item); + T::__push_back(self, item); } } @@ -66,13 +66,13 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { #[doc(hidden)] pub trait VectorElement { - fn get_unchecked(v: &CxxVector, pos: usize) -> &T + fn __get_unchecked(v: &CxxVector, pos: usize) -> &T where Self: Sized; - fn vector_length(v: &CxxVector) -> usize + fn __vector_length(v: &CxxVector) -> usize where Self: Sized; - fn push_back(v: &CxxVector, item: &T) + fn __push_back(v: &CxxVector, item: &T) where Self: Sized; } From 5104c8658b08cc6ab696d302055272afb3412dee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 383/2232] Clarify that VectorElement is not meant to be implemented --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 477495a..657bcd2 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -655,7 +655,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - impl ::cxx::private::VectorElement<#inner> for #inner { + unsafe impl ::cxx::private::VectorElement<#inner> for #inner { fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] @@ -698,7 +698,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - impl VectorElement<#inner> for #inner { + unsafe impl VectorElement<#inner> for #inner { fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 7db8a45..c3f4fe7 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -64,8 +64,10 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { } } +// Methods are private; not intended to be implemented outside of cxxbridge +// codebase. #[doc(hidden)] -pub trait VectorElement { +pub unsafe trait VectorElement { fn __get_unchecked(v: &CxxVector, pos: usize) -> &T where Self: Sized; From 33050ece6badd78a68f1ac24efa9c326e99a0144 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 384/2232] Remove unneeded vector element Sized constraints We only implement VectorElement for sized types anyway. --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index c3f4fe7..c0d2f6b 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -68,15 +68,9 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { // codebase. #[doc(hidden)] pub unsafe trait VectorElement { - fn __get_unchecked(v: &CxxVector, pos: usize) -> &T - where - Self: Sized; - fn __vector_length(v: &CxxVector) -> usize - where - Self: Sized; - fn __push_back(v: &CxxVector, item: &T) - where - Self: Sized; + fn __get_unchecked(v: &CxxVector, pos: usize) -> &T; + fn __vector_length(v: &CxxVector) -> usize; + fn __push_back(v: &CxxVector, item: &T); } cxxbridge_macro::vector_builtin!(u8); From 31ad0be65398b5fbc746a24f9011b040fd5b7d19 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 385/2232] Unsafe CxxVector::get_unchecked --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 657bcd2..1b3aab3 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -656,14 +656,12 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { quote! { unsafe impl ::cxx::private::VectorElement<#inner> for #inner { - fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { + unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; } - unsafe { - __get_unchecked(v, pos) - } + __get_unchecked(v, pos) } fn __vector_length(v: &::cxx::CxxVector<#inner>) -> usize { unsafe { @@ -699,14 +697,12 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { quote! { unsafe impl VectorElement<#inner> for #inner { - fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { + unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; } - unsafe { - __get_unchecked(v, pos) - } + __get_unchecked(v, pos) } fn __vector_length(v: &CxxVector<#inner>) -> usize { unsafe { diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index c0d2f6b..7b985d8 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -19,7 +19,7 @@ impl> CxxVector { T::__vector_length(self) } - pub fn get_unchecked(&self, pos: usize) -> &T { + pub unsafe fn get_unchecked(&self, pos: usize) -> &T { T::__get_unchecked(self, pos) } @@ -30,7 +30,7 @@ impl> CxxVector { pub fn get(&self, pos: usize) -> Option<&T> { if pos < self.size() { - Some(self.get_unchecked(pos)) + Some(unsafe { T::__get_unchecked(self, pos) }) } else { None } @@ -68,7 +68,7 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { // codebase. #[doc(hidden)] pub unsafe trait VectorElement { - fn __get_unchecked(v: &CxxVector, pos: usize) -> &T; + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &T; fn __vector_length(v: &CxxVector) -> usize; fn __push_back(v: &CxxVector, item: &T); } From c01d0a0f8470416ff617b5097773345b53e6caa6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 386/2232] Rename CxxVector::size to len to match Rust conventions --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 7b985d8..bf9f5fa 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -15,7 +15,7 @@ pub struct CxxVector { impl> CxxVector { /// Returns the length of the vector in bytes. - pub fn size(&self) -> usize { + pub fn len(&self) -> usize { T::__vector_length(self) } @@ -25,11 +25,11 @@ impl> CxxVector { /// Returns true if `self` has a length of zero bytes. pub fn is_empty(&self) -> bool { - self.size() == 0 + self.len() == 0 } pub fn get(&self, pos: usize) -> Option<&T> { - if pos < self.size() { + if pos < self.len() { Some(unsafe { T::__get_unchecked(self, pos) }) } else { None diff --git a/tests/test.rs b/tests/test.rs index f394c7a..1d903f0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -32,7 +32,7 @@ fn test_c_return() { assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); - assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().size()); + assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len()); assert_eq!( 200_u8, ffi::c_return_unique_ptr_vector_u8().into_iter().sum(), @@ -41,7 +41,7 @@ fn test_c_return() { 200.5_f64, ffi::c_return_unique_ptr_vector_f64().into_iter().sum(), ); - assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().size()); + assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len()); assert_eq!( 2021_usize, ffi::c_return_unique_ptr_vector_shared() From c3ed3a67aa07a9b6473924095276caa71c3416ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 387/2232] Remove redundant type parameter of VectorElement --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1b3aab3..510fcca 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -655,7 +655,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - unsafe impl ::cxx::private::VectorElement<#inner> for #inner { + unsafe impl ::cxx::private::VectorElement for #inner { unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] @@ -696,7 +696,7 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { let link_push_back = format!("{}push_back", prefix); quote! { - unsafe impl VectorElement<#inner> for #inner { + unsafe impl VectorElement for #inner { unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index bf9f5fa..5c7c46c 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -13,7 +13,7 @@ pub struct CxxVector { _private: [T; 0], } -impl> CxxVector { +impl CxxVector { /// Returns the length of the vector in bytes. pub fn len(&self) -> usize { T::__vector_length(self) @@ -46,7 +46,7 @@ pub struct VectorIntoIterator<'a, T> { index: usize, } -impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { +impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { type Item = &'a T; type IntoIter = VectorIntoIterator<'a, T>; @@ -55,7 +55,7 @@ impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { } } -impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { +impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { @@ -67,10 +67,10 @@ impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { // Methods are private; not intended to be implemented outside of cxxbridge // codebase. #[doc(hidden)] -pub unsafe trait VectorElement { - unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &T; - fn __vector_length(v: &CxxVector) -> usize; - fn __push_back(v: &CxxVector, item: &T); +pub unsafe trait VectorElement: Sized { + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; + fn __vector_length(v: &CxxVector) -> usize; + fn __push_back(v: &CxxVector, item: &Self); } cxxbridge_macro::vector_builtin!(u8); From cdc8796d62ac42e879488010f10f6d65acde2e1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 388/2232] Document CxxVector associated methods --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 5c7c46c..4a5e8e6 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -14,20 +14,27 @@ pub struct CxxVector { } impl CxxVector { - /// Returns the length of the vector in bytes. + /// Returns the number of elements in the vector. pub fn len(&self) -> usize { T::__vector_length(self) } + /// Returns a reference to an element without doing bounds checking. + /// + /// This is generally not recommended, use with caution! Calling this method + /// with an out-of-bounds index is undefined behavior even if the resulting + /// reference is not used. pub unsafe fn get_unchecked(&self, pos: usize) -> &T { T::__get_unchecked(self, pos) } - /// Returns true if `self` has a length of zero bytes. + /// Returns true if the vector contains no elements. pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Returns a reference to an element at the given position, or `None` if + /// out of bounds. pub fn get(&self, pos: usize) -> Option<&T> { if pos < self.len() { Some(unsafe { T::__get_unchecked(self, pos) }) @@ -36,6 +43,7 @@ impl CxxVector { } } + /// Appends an element to the back of the vector. pub fn push_back(&mut self, item: &T) { T::__push_back(self, item); } From 4944f2f07310cd679812b95a8fc33e7a982b5c46 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:10 +0000 Subject: [PATCH 389/2232] Move get_unchecked after safe get method --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 4a5e8e6..02069e3 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -19,15 +19,6 @@ impl CxxVector { T::__vector_length(self) } - /// Returns a reference to an element without doing bounds checking. - /// - /// This is generally not recommended, use with caution! Calling this method - /// with an out-of-bounds index is undefined behavior even if the resulting - /// reference is not used. - pub unsafe fn get_unchecked(&self, pos: usize) -> &T { - T::__get_unchecked(self, pos) - } - /// Returns true if the vector contains no elements. pub fn is_empty(&self) -> bool { self.len() == 0 @@ -43,6 +34,15 @@ impl CxxVector { } } + /// Returns a reference to an element without doing bounds checking. + /// + /// This is generally not recommended, use with caution! Calling this method + /// with an out-of-bounds index is undefined behavior even if the resulting + /// reference is not used. + pub unsafe fn get_unchecked(&self, pos: usize) -> &T { + T::__get_unchecked(self, pos) + } + /// Appends an element to the back of the vector. pub fn push_back(&mut self, item: &T) { T::__push_back(self, item); From 3d88bdc9875c5ff1df5976505a192fa14ce145a4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 390/2232] Rename VectorIntoIterator to Iter This matches the name of std::slice::Iter. --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 02069e3..862c990 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -49,21 +49,21 @@ impl CxxVector { } } -pub struct VectorIntoIterator<'a, T> { +pub struct Iter<'a, T> { v: &'a CxxVector, index: usize, } impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { type Item = &'a T; - type IntoIter = VectorIntoIterator<'a, T>; + type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - VectorIntoIterator { v: self, index: 0 } + Iter { v: self, index: 0 } } } -impl<'a, T: VectorElement> Iterator for VectorIntoIterator<'a, T> { +impl<'a, T: VectorElement> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { From 0e08466bbba3e64c38edbff89774973c263d7c5f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 391/2232] Rename to vector_size to match std::vector<>::size --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 510fcca..8eb6fe8 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -663,13 +663,13 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { } __get_unchecked(v, pos) } - fn __vector_length(v: &::cxx::CxxVector<#inner>) -> usize { + fn __vector_size(v: &::cxx::CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] - fn __vector_length(_: &::cxx::CxxVector<#inner>) -> usize; + fn __vector_size(_: &::cxx::CxxVector<#inner>) -> usize; } - __vector_length(v) + __vector_size(v) } } fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { @@ -704,13 +704,13 @@ pub fn expand_vector_builtin(ident: Ident) -> TokenStream { } __get_unchecked(v, pos) } - fn __vector_length(v: &CxxVector<#inner>) -> usize { + fn __vector_size(v: &CxxVector<#inner>) -> usize { unsafe { extern "C" { #[link_name = #link_length] - fn __vector_length(_: &CxxVector<#inner>) -> usize; + fn __vector_size(_: &CxxVector<#inner>) -> usize; } - __vector_length(v) + __vector_size(v) } } fn __push_back(v: &CxxVector<#inner>, item: &#inner) { diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 862c990..f5db334 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -16,7 +16,7 @@ pub struct CxxVector { impl CxxVector { /// Returns the number of elements in the vector. pub fn len(&self) -> usize { - T::__vector_length(self) + T::__vector_size(self) } /// Returns true if the vector contains no elements. @@ -77,7 +77,7 @@ impl<'a, T: VectorElement> Iterator for Iter<'a, T> { #[doc(hidden)] pub unsafe trait VectorElement: Sized { unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; - fn __vector_length(v: &CxxVector) -> usize; + fn __vector_size(v: &CxxVector) -> usize; fn __push_back(v: &CxxVector, item: &Self); } From 147fcc501ae158fd0002a850662e92fcc6591af2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 392/2232] Call vector_builtin macro via import --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index f5db334..2aae4a8 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,3 +1,4 @@ +use cxxbridge_macro::vector_builtin; use std::mem; /// Binding to C++ `std::vector`. @@ -81,17 +82,17 @@ pub unsafe trait VectorElement: Sized { fn __push_back(v: &CxxVector, item: &Self); } -cxxbridge_macro::vector_builtin!(u8); -cxxbridge_macro::vector_builtin!(u16); -cxxbridge_macro::vector_builtin!(u32); -cxxbridge_macro::vector_builtin!(u64); -cxxbridge_macro::vector_builtin!(usize); -cxxbridge_macro::vector_builtin!(i8); -cxxbridge_macro::vector_builtin!(i16); -cxxbridge_macro::vector_builtin!(i32); -cxxbridge_macro::vector_builtin!(i64); -cxxbridge_macro::vector_builtin!(isize); -cxxbridge_macro::vector_builtin!(f32); -cxxbridge_macro::vector_builtin!(f64); +vector_builtin!(u8); +vector_builtin!(u16); +vector_builtin!(u32); +vector_builtin!(u64); +vector_builtin!(usize); +vector_builtin!(i8); +vector_builtin!(i16); +vector_builtin!(i32); +vector_builtin!(i64); +vector_builtin!(isize); +vector_builtin!(f32); +vector_builtin!(f64); const_assert_eq!(1, mem::align_of::>()); From 4b91eaaf7fca9df3d9cb49e114e1361bce9ed2f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 393/2232] More explicit naming for vector element primitive macro --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8eb6fe8..be23fcf 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -685,7 +685,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { } } -pub fn expand_vector_builtin(ident: Ident) -> TokenStream { +pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { let ty = Type::Ident(ident); let inner = &ty; let namespace = Namespace { segments: vec![] }; diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 87fe405..e6e5f2e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -46,7 +46,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { } #[proc_macro] -pub fn vector_builtin(input: TokenStream) -> TokenStream { +pub fn impl_vector_element_for_primitive(input: TokenStream) -> TokenStream { let ident = parse_macro_input!(input as Ident); - expand::expand_vector_builtin(ident).into() + expand::impl_vector_element_for_primitive(ident).into() } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 2aae4a8..3742fa4 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,4 +1,4 @@ -use cxxbridge_macro::vector_builtin; +use cxxbridge_macro::impl_vector_element_for_primitive; use std::mem; /// Binding to C++ `std::vector`. @@ -82,17 +82,17 @@ pub unsafe trait VectorElement: Sized { fn __push_back(v: &CxxVector, item: &Self); } -vector_builtin!(u8); -vector_builtin!(u16); -vector_builtin!(u32); -vector_builtin!(u64); -vector_builtin!(usize); -vector_builtin!(i8); -vector_builtin!(i16); -vector_builtin!(i32); -vector_builtin!(i64); -vector_builtin!(isize); -vector_builtin!(f32); -vector_builtin!(f64); +impl_vector_element_for_primitive!(u8); +impl_vector_element_for_primitive!(u16); +impl_vector_element_for_primitive!(u32); +impl_vector_element_for_primitive!(u64); +impl_vector_element_for_primitive!(usize); +impl_vector_element_for_primitive!(i8); +impl_vector_element_for_primitive!(i16); +impl_vector_element_for_primitive!(i32); +impl_vector_element_for_primitive!(i64); +impl_vector_element_for_primitive!(isize); +impl_vector_element_for_primitive!(f32); +impl_vector_element_for_primitive!(f64); const_assert_eq!(1, mem::align_of::>()); From a83247c5a3d79a414015ebda9108fb3400919849 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 394/2232] Rename std$vector$T$length to match std::vector::size --- diff --git a/gen/write.rs b/gen/write.rs index c1aa2f7..3da42b3 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1131,7 +1131,7 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); writeln!( out, - "size_t cxxbridge02$std$vector${}$length(const ::std::vector<{}> &s) noexcept {{", + "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index be23fcf..b2278e0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -650,7 +650,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let inner = ty; let mangled = ty.to_mangled(&namespace.segments) + "$"; let prefix = format!("cxxbridge02$std$vector${}", mangled); - let link_length = format!("{}length", prefix); + let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); @@ -666,7 +666,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { fn __vector_size(v: &::cxx::CxxVector<#inner>) -> usize { unsafe { extern "C" { - #[link_name = #link_length] + #[link_name = #link_size] fn __vector_size(_: &::cxx::CxxVector<#inner>) -> usize; } __vector_size(v) @@ -691,7 +691,7 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { let namespace = Namespace { segments: vec![] }; let mangled = ty.to_mangled(&namespace.segments) + "$"; let prefix = format!("cxxbridge02$std$vector${}", mangled); - let link_length = format!("{}length", prefix); + let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); @@ -707,7 +707,7 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { fn __vector_size(v: &CxxVector<#inner>) -> usize { unsafe { extern "C" { - #[link_name = #link_length] + #[link_name = #link_size] fn __vector_size(_: &CxxVector<#inner>) -> usize; } __vector_size(v) diff --git a/src/cxx.cc b/src/cxx.cc index a0e92ac..15f888d 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -201,7 +201,7 @@ void cxxbridge02$unique_ptr$std$string$drop( #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ extern "C" { \ - size_t cxxbridge02$std$vector$##RUST_TYPE##$length( \ + size_t cxxbridge02$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ } \ From 61677aa8135912377e03bd35e69bf4f267692363 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 395/2232] Fix push_back extern decl to match the C++ signature --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index b2278e0..8ec1107 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -676,7 +676,7 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { unsafe { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner) -> usize; + fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner); } __push_back(v, item); } @@ -717,7 +717,7 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { unsafe { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &CxxVector<#inner>, _: &#inner) -> usize; + fn __push_back(_: &CxxVector<#inner>, _: &#inner); } __push_back(v, item); } From dd3f6344e77b3d0bc2ee9c4f6f909472c5b33db2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 396/2232] Fix get_unchecked impl signature to match Rust --- diff --git a/gen/write.rs b/gen/write.rs index 3da42b3..2db985c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1147,10 +1147,10 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { writeln!( out, - "const {} *cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); - writeln!(out, " return &s[pos];"); + writeln!(out, " return s[pos];"); writeln!(out, "}}"); writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); } diff --git a/src/cxx.cc b/src/cxx.cc index 15f888d..e716aee 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -209,9 +209,9 @@ void cxxbridge02$unique_ptr$std$string$drop( std::vector &s, const CXX_TYPE &item) noexcept { \ s.push_back(item); \ } \ - const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ + const CXX_TYPE &cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ - return &s[pos]; \ + return s[pos]; \ } \ static_assert(sizeof(::std::unique_ptr>) == \ sizeof(void *), \ From fa2119c886a22c943fd05ef6e9ff07d20b0c0733 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:11 +0000 Subject: [PATCH 397/2232] Make some unsafe blocks smaller in vector generated code --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8ec1107..3aeb831 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -664,22 +664,18 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { __get_unchecked(v, pos) } fn __vector_size(v: &::cxx::CxxVector<#inner>) -> usize { - unsafe { - extern "C" { - #[link_name = #link_size] - fn __vector_size(_: &::cxx::CxxVector<#inner>) -> usize; - } - __vector_size(v) + extern "C" { + #[link_name = #link_size] + fn __vector_size(_: &::cxx::CxxVector<#inner>) -> usize; } + unsafe { __vector_size(v) } } fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { - unsafe { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner); - } - __push_back(v, item); + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner); } + unsafe { __push_back(v, item) } } } } @@ -705,22 +701,18 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { __get_unchecked(v, pos) } fn __vector_size(v: &CxxVector<#inner>) -> usize { - unsafe { - extern "C" { - #[link_name = #link_size] - fn __vector_size(_: &CxxVector<#inner>) -> usize; - } - __vector_size(v) + extern "C" { + #[link_name = #link_size] + fn __vector_size(_: &CxxVector<#inner>) -> usize; } + unsafe { __vector_size(v) } } fn __push_back(v: &CxxVector<#inner>, item: &#inner) { - unsafe { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &CxxVector<#inner>, _: &#inner); - } - __push_back(v, item); + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &CxxVector<#inner>, _: &#inner); } + unsafe { __push_back(v, item) } } } } From cc75ad201ed818769f2039737047988d9d79620d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 398/2232] Reorder vector-element methods to a consistent order --- diff --git a/gen/write.rs b/gen/write.rs index 2db985c..6c43928 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1136,21 +1136,19 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { ); writeln!(out, " return s.size();"); writeln!(out, "}}"); - writeln!( out, - "void cxxbridge02$std$vector${}$push_back(::std::vector<{}> &s, const {} &item) noexcept {{", - instance, inner, inner + "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, ); - writeln!(out, " s.push_back(item);"); + writeln!(out, " return s[pos];"); writeln!(out, "}}"); - writeln!( out, - "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", - inner, instance, inner, + "void cxxbridge02$std$vector${}$push_back(::std::vector<{}> &s, const {} &item) noexcept {{", + instance, inner, inner ); - writeln!(out, " return s[pos];"); + writeln!(out, " s.push_back(item);"); writeln!(out, "}}"); writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 3aeb831..6ec40e0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -656,13 +656,6 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { quote! { unsafe impl ::cxx::private::VectorElement for #inner { - unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { - extern "C" { - #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; - } - __get_unchecked(v, pos) - } fn __vector_size(v: &::cxx::CxxVector<#inner>) -> usize { extern "C" { #[link_name = #link_size] @@ -670,6 +663,13 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { } unsafe { __vector_size(v) } } + unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; + } + __get_unchecked(v, pos) + } fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { extern "C" { #[link_name = #link_push_back] @@ -693,13 +693,6 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { quote! { unsafe impl VectorElement for #inner { - unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { - extern "C" { - #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; - } - __get_unchecked(v, pos) - } fn __vector_size(v: &CxxVector<#inner>) -> usize { extern "C" { #[link_name = #link_size] @@ -707,6 +700,13 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { } unsafe { __vector_size(v) } } + unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; + } + __get_unchecked(v, pos) + } fn __push_back(v: &CxxVector<#inner>, item: &#inner) { extern "C" { #[link_name = #link_push_back] diff --git a/src/cxx.cc b/src/cxx.cc index e716aee..f1cc455 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -205,14 +205,14 @@ void cxxbridge02$unique_ptr$std$string$drop( const std::vector &s) noexcept { \ return s.size(); \ } \ - void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ - std::vector &s, const CXX_TYPE &item) noexcept { \ - s.push_back(item); \ - } \ const CXX_TYPE &cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ return s[pos]; \ } \ + void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ + std::vector &s, const CXX_TYPE &item) noexcept { \ + s.push_back(item); \ + } \ static_assert(sizeof(::std::unique_ptr>) == \ sizeof(void *), \ ""); \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 3742fa4..829e12a 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -77,8 +77,8 @@ impl<'a, T: VectorElement> Iterator for Iter<'a, T> { // codebase. #[doc(hidden)] pub unsafe trait VectorElement: Sized { - unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; fn __vector_size(v: &CxxVector) -> usize; + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; fn __push_back(v: &CxxVector, item: &Self); } From 996db1efb1e77b4c17517dd04981d262d4e8e17c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 399/2232] Remove unneeded absolute paths in cxx.cc vector code --- diff --git a/src/cxx.cc b/src/cxx.cc index f1cc455..a3ff557 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -213,39 +213,39 @@ void cxxbridge02$unique_ptr$std$string$drop( std::vector &s, const CXX_TYPE &item) noexcept { \ s.push_back(item); \ } \ - static_assert(sizeof(::std::unique_ptr>) == \ + static_assert(sizeof(std::unique_ptr>) == \ sizeof(void *), \ ""); \ - static_assert(alignof(::std::unique_ptr>) == \ + static_assert(alignof(std::unique_ptr>) == \ alignof(void *), \ ""); \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ - ::std::unique_ptr> *ptr) noexcept { \ - new (ptr)::std::unique_ptr>(); \ + std::unique_ptr> *ptr) noexcept { \ + new (ptr) std::unique_ptr>(); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new( \ - ::std::unique_ptr> *ptr, \ + std::unique_ptr> *ptr, \ std::vector *value) noexcept { \ - new (ptr)::std::unique_ptr>( \ - new std::vector(::std::move(*value))); \ + new (ptr) std::unique_ptr>( \ + new std::vector(std::move(*value))); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ - ::std::unique_ptr> *ptr, \ + std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ - new (ptr)::std::unique_ptr>(raw); \ + new (ptr) std::unique_ptr>(raw); \ } \ const std::vector * \ cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ - const ::std::unique_ptr> &ptr) noexcept { \ + const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ std::vector * \ cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ - ::std::unique_ptr> &ptr) noexcept { \ + std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ - ::std::unique_ptr> *ptr) noexcept { \ + std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ } \ } // extern "C" From 4e7e7c47cfc8f5f47890d5998bd6d189cc9c7710 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 400/2232] Collect std vector ops into one extern "C" block --- diff --git a/src/cxx.cc b/src/cxx.cc index a3ff557..d9be1fb 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -200,7 +200,6 @@ void cxxbridge02$unique_ptr$std$string$drop( } // extern "C" #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ - extern "C" { \ size_t cxxbridge02$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ @@ -213,12 +212,10 @@ void cxxbridge02$unique_ptr$std$string$drop( std::vector &s, const CXX_TYPE &item) noexcept { \ s.push_back(item); \ } \ - static_assert(sizeof(std::unique_ptr>) == \ - sizeof(void *), \ - ""); \ - static_assert(alignof(std::unique_ptr>) == \ - alignof(void *), \ - ""); \ + static_assert( \ + sizeof(std::unique_ptr>) == sizeof(void *), ""); \ + static_assert( \ + alignof(std::unique_ptr>) == alignof(void *), ""); \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ @@ -234,22 +231,22 @@ void cxxbridge02$unique_ptr$std$string$drop( std::vector *raw) noexcept { \ new (ptr) std::unique_ptr>(raw); \ } \ - const std::vector * \ - cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const std::vector \ + *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ - std::vector * \ - cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ + std::vector \ + *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ - } \ - } // extern "C" + } +extern "C" { STD_VECTOR_OPS(u8, uint8_t); STD_VECTOR_OPS(u16, uint16_t); STD_VECTOR_OPS(u32, uint32_t); @@ -262,3 +259,4 @@ STD_VECTOR_OPS(i64, int64_t); STD_VECTOR_OPS(isize, rust::isize); STD_VECTOR_OPS(f32, float); STD_VECTOR_OPS(f64, double); +} // extern "C" From 9626d081b59be90aa24316aa4fe8ecf95fdf52c0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 401/2232] Update get_unchecked to void Wreturn-type-c-linkage Otherwise: 'cxxbridge02$std$vector$u8$get_unchecked' has C-linkage specified, but returns user-defined type 'const uint8_t &' (aka 'const unsigned char &') which is incompatible with C [-Wreturn-type-c-linkage] --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6ec40e0..04003a0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -666,9 +666,9 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner; + fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> *const #inner; } - __get_unchecked(v, pos) + &*__get_unchecked(v, pos) } fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { extern "C" { @@ -703,9 +703,9 @@ pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner; + fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> *const #inner; } - __get_unchecked(v, pos) + &*__get_unchecked(v, pos) } fn __push_back(v: &CxxVector<#inner>, item: &#inner) { extern "C" { diff --git a/src/cxx.cc b/src/cxx.cc index d9be1fb..b9e4826 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -204,9 +204,9 @@ void cxxbridge02$unique_ptr$std$string$drop( const std::vector &s) noexcept { \ return s.size(); \ } \ - const CXX_TYPE &cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ + const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ - return s[pos]; \ + return &s[pos]; \ } \ void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ std::vector &s, const CXX_TYPE &item) noexcept { \ From e4b6a62d337fc02fd48ea2c796718bc2db2f9103 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 402/2232] Move vector element primitive macro to macro_rules --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 04003a0..1966d7a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -681,43 +681,6 @@ fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { } } -pub fn impl_vector_element_for_primitive(ident: Ident) -> TokenStream { - let ty = Type::Ident(ident); - let inner = &ty; - let namespace = Namespace { segments: vec![] }; - let mangled = ty.to_mangled(&namespace.segments) + "$"; - let prefix = format!("cxxbridge02$std$vector${}", mangled); - let link_size = format!("{}size", prefix); - let link_get_unchecked = format!("{}get_unchecked", prefix); - let link_push_back = format!("{}push_back", prefix); - - quote! { - unsafe impl VectorElement for #inner { - fn __vector_size(v: &CxxVector<#inner>) -> usize { - extern "C" { - #[link_name = #link_size] - fn __vector_size(_: &CxxVector<#inner>) -> usize; - } - unsafe { __vector_size(v) } - } - unsafe fn __get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner { - extern "C" { - #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> *const #inner; - } - &*__get_unchecked(v, pos) - } - fn __push_back(v: &CxxVector<#inner>, item: &#inner) { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &CxxVector<#inner>, _: &#inner); - } - unsafe { __push_back(v, item) } - } - } - } -} - fn expand_return_type(ret: &Option) -> TokenStream { match ret { Some(ret) => quote!(-> #ret), diff --git a/macro/src/lib.rs b/macro/src/lib.rs index e6e5f2e..b56f58e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,7 +14,7 @@ mod syntax; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, Ident, ItemMod}; +use syn::{parse_macro_input, ItemMod}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,9 +44,3 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } - -#[proc_macro] -pub fn impl_vector_element_for_primitive(input: TokenStream) -> TokenStream { - let ident = parse_macro_input!(input as Ident); - expand::impl_vector_element_for_primitive(ident).into() -} diff --git a/src/concat.rs b/src/concat.rs new file mode 100644 index 0000000..e67e50d --- /dev/null +++ b/src/concat.rs @@ -0,0 +1,6 @@ +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 829e12a..f7fb5ac 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,4 +1,3 @@ -use cxxbridge_macro::impl_vector_element_for_primitive; use std::mem; /// Binding to C++ `std::vector`. @@ -82,6 +81,40 @@ pub unsafe trait VectorElement: Sized { fn __push_back(v: &CxxVector, item: &Self); } +macro_rules! impl_vector_element_for_primitive { + ($ty:ident) => { + unsafe impl VectorElement for $ty { + fn __vector_size(v: &CxxVector<$ty>) -> usize { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$size")] + fn __vector_size(_: &CxxVector<$ty>) -> usize; + } + } + unsafe { __vector_size(v) } + } + unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$get_unchecked")] + fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; + } + } + &*__get_unchecked(v, pos) + } + fn __push_back(v: &CxxVector<$ty>, item: &$ty) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")] + fn __push_back(_: &CxxVector<$ty>, _: &$ty); + } + } + unsafe { __push_back(v, item) } + } + } + }; +} + impl_vector_element_for_primitive!(u8); impl_vector_element_for_primitive!(u16); impl_vector_element_for_primitive!(u32); diff --git a/src/lib.rs b/src/lib.rs index 1af4d0f..c9f4667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -358,6 +358,8 @@ extern crate link_cplusplus; #[macro_use] mod assert; +#[macro_use] +mod concat; mod cxx_string; mod cxx_vector; From 4377a9e8379fe45b4404700ecd93e7dd3eb3c0c8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 403/2232] Rename Type::Vector to CxxVector --- diff --git a/gen/write.rs b/gen/write.rs index 6c43928..deae886 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -125,7 +125,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, - Type::Vector(_) => out.include.vector = true, + Type::CxxVector(_) => out.include.vector = true, Type::SliceRefU8(_) => out.include.cstdint = true, _ => {} } @@ -478,7 +478,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Vector(_)) => write!( + Some(Type::CxxVector(_)) => write!( out, " /* Use RVO to convert to r-value and move construct */" ), @@ -792,7 +792,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -827,7 +827,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &ptr.inner); write!(out, ">"); } - Type::Vector(ty) => { + Type::CxxVector(ty) => { write!(out, "::std::vector<"); write_type(out, &ty.inner); write!(out, ">"); @@ -879,7 +879,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) - | Type::Vector(_) + | Type::CxxVector(_) | Type::RustVec(_) | Type::SliceRefU8(_) | Type::Fn(_) => write!(out, " "), @@ -916,7 +916,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.next_section(); write_unique_ptr(out, &ptr.inner, types); } - } else if let Type::Vector(ptr1) = &ptr.inner { + } else if let Type::CxxVector(ptr1) = &ptr.inner { if let Type::Ident(inner) = &ptr1.inner { if allow_vector(inner) { out.next_section(); @@ -924,7 +924,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - } else if let Type::Vector(ptr) = ty { + } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if allow_vector(inner) { out.next_section(); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1966d7a..9b17b11 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -64,7 +64,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { if Atom::from(ident).is_none() { expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); } - } else if let Type::Vector(_) = &ptr.inner { + } else if let Type::CxxVector(_) = &ptr.inner { // Generate code for unique_ptr> if T is not an atom // or if T is a primitive. // Code for primitives is already generated @@ -77,7 +77,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } } } - } else if let Type::Vector(ptr) = ty { + } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { // Generate code for CxxVector if T is not an atom diff --git a/syntax/check.rs b/syntax/check.rs index 387f9a6..cbfa91b 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -31,7 +31,7 @@ fn do_typecheck(cx: &mut Check) { Type::RustBox(ptr) => check_type_box(cx, ptr), Type::RustVec(ptr) => check_type_vec(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), - Type::Vector(ptr) => check_type_vector(cx, ptr), + Type::CxxVector(ptr) => check_type_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Slice(ty) => check_type_slice(cx, ty), _ => {} @@ -114,7 +114,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(CxxString) => return, _ => {} } - } else if let Type::Vector(_) = &ptr.inner { + } else if let Type::CxxVector(_) = &ptr.inner { return; } @@ -351,7 +351,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), - Type::Vector(_) => "vector".to_owned(), + Type::CxxVector(_) => "vector".to_owned(), Type::Slice(_) => "slice".to_owned(), Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), diff --git a/syntax/impls.rs b/syntax/impls.rs index 8e94f06..c34e3e5 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -27,7 +27,7 @@ impl Hash for Type { Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::RustVec(t) => t.hash(state), - Type::Vector(t) => t.hash(state), + Type::CxxVector(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::Slice(t) => t.hash(state), Type::SliceRefU8(t) => t.hash(state), @@ -47,7 +47,7 @@ impl PartialEq for Type { (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::Vector(lhs), Type::Vector(rhs)) => lhs == rhs, + (Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, diff --git a/syntax/mangled.rs b/syntax/mangled.rs index 56e8b73..2a2fa4c 100644 --- a/syntax/mangled.rs +++ b/syntax/mangled.rs @@ -23,7 +23,7 @@ impl ToMangled for Type { Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)), Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)), Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)), - Type::Vector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), + Type::CxxVector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), _ => unimplemented!(), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 4e0b908..c992690 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -92,7 +92,7 @@ pub enum Type { UniquePtr(Box), Ref(Box), Str(Box), - Vector(Box), + CxxVector(Box), Fn(Box), Void(Span), Slice(Box), diff --git a/syntax/parse.rs b/syntax/parse.rs index f5c598d..f32894b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -303,7 +303,7 @@ fn parse_type_path(ty: &TypePath) -> Result { } else if ident == "Vector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; - return Ok(Type::Vector(Box::new(Ty1 { + return Ok(Type::CxxVector(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 3d67a0a..7423f8e 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -14,7 +14,7 @@ impl ToTokens for Type { } ident.to_tokens(tokens); } - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => { ty.to_tokens(tokens) } Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), diff --git a/syntax/typename.rs b/syntax/typename.rs index 7ad1be2..016749a 100644 --- a/syntax/typename.rs +++ b/syntax/typename.rs @@ -29,7 +29,7 @@ impl ToTypename for Type { Type::UniquePtr(ptr) => { format!("::std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) } - Type::Vector(ptr) => format!("::std::vector<{}>", ptr.inner.to_typename(namespace)), + Type::CxxVector(ptr) => format!("::std::vector<{}>", ptr.inner.to_typename(namespace)), _ => unimplemented!(), } } diff --git a/syntax/types.rs b/syntax/types.rs index 7bce154..2ef1e1e 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -24,9 +24,10 @@ impl<'a> Types<'a> { all.insert(ty); match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { - visit(all, &ty.inner) - } + Type::RustBox(ty) + | Type::UniquePtr(ty) + | Type::CxxVector(ty) + | Type::RustVec(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { From 9c6bf2d8ef405d42257de71f2928527c81424d3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 404/2232] Remove assumption of Vec field order --- diff --git a/gen/write.rs b/gen/write.rs index deae886..faf3311 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -147,6 +147,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_box = true; } Type::RustVec(_) => { + out.include.array = true; needs_rust_vec = true; } Type::Str(_) => { diff --git a/include/cxx.h b/include/cxx.h index c62faad..1df6c9b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -97,10 +97,8 @@ private: Vec &operator=(Vec other) noexcept; void drop() noexcept; - // Repr - const T *ptr; - size_t len; - size_t capacity; + // Size and alignment statically verified by rust_vec.rs. + std::array repr; }; #endif // CXXBRIDGE02_RUST_VEC diff --git a/src/rust_vec.rs b/src/rust_vec.rs index a8a144a..1c0c64f 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,3 +1,5 @@ +use std::mem; + #[repr(C)] pub struct RustVec { repr: Vec, @@ -28,3 +30,6 @@ impl RustVec { self.repr.len() } } + +const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); +const_assert_eq!(mem::align_of::(), mem::align_of::>()); From 347c3d01f4d6f8e23a75703be85be3998d7be1e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:12 +0000 Subject: [PATCH 405/2232] Add Vec and std::vector to builtin types list --- diff --git a/README.md b/README.md index a8f4093..f967762 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,8 @@ returns of functions. CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +Vec<T>rust::Vec<T>cannot hold opaque C++ type +CxxVector<T>std::vector<T>cannot hold opaque Rust type fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far Result<T>throw/catchallowed as return type only @@ -317,11 +319,9 @@ matter of designing a nice API for each in its non-native language. - - diff --git a/src/lib.rs b/src/lib.rs index c9f4667..e4ef8f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -311,6 +311,8 @@ //! //! //! +//! +//! //! //! //!
name in Rustname in C++
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
tbdstd::shared_ptr<T>
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot hold opaque Rust type
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
@@ -325,11 +327,9 @@ //! //! //! -//! //! //! //! -//! //! //! //! From 61a9fdf313f7880669b6812cc0861bed3e95d970 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 406/2232] Be explit about binding only default vector allocator --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index f7fb5ac..8a73c7e 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,6 +1,6 @@ use std::mem; -/// Binding to C++ `std::vector`. +/// Binding to C++ `std::vector>`. /// /// # Invariants /// From dd8391916fa9dc0ab527a0a35aa3f0f906d834b0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 407/2232] Add C++ documentation links to CxxVector --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 8a73c7e..c30dd02 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -15,11 +15,19 @@ pub struct CxxVector { impl CxxVector { /// Returns the number of elements in the vector. + /// + /// Matches the behavior of C++ [std::vector\::size][size]. + /// + /// [size]: https://en.cppreference.com/w/cpp/container/vector/size pub fn len(&self) -> usize { T::__vector_size(self) } /// Returns true if the vector contains no elements. + /// + /// Matches the behavior of C++ [std::vector\::empty][empty]. + /// + /// [empty]: https://en.cppreference.com/w/cpp/container/vector/empty pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -39,6 +47,11 @@ impl CxxVector { /// This is generally not recommended, use with caution! Calling this method /// with an out-of-bounds index is undefined behavior even if the resulting /// reference is not used. + /// + /// Matches the behavior of C++ + /// [std::vector\::operator\[\]][operator_at]. + /// + /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at pub unsafe fn get_unchecked(&self, pos: usize) -> &T { T::__get_unchecked(self, pos) } From 7f2dc3bab00a0cd2c5743bb731bfe89f7d49fa51 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 408/2232] Reorder rust::Vec below Box --- diff --git a/include/cxx.h b/include/cxx.h index 1df6c9b..a96e6fa 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -84,24 +84,6 @@ private: }; #endif // CXXBRIDGE02_RUST_STR -#ifndef CXXBRIDGE02_RUST_VEC -#define CXXBRIDGE02_RUST_VEC -template -class Vec final { -public: - size_t size() const noexcept; - -private: - Vec() noexcept; - Vec(const Vec &other) noexcept; - Vec &operator=(Vec other) noexcept; - void drop() noexcept; - - // Size and alignment statically verified by rust_vec.rs. - std::array repr; -}; -#endif // CXXBRIDGE02_RUST_VEC - #ifndef CXXBRIDGE02_RUST_SLICE #define CXXBRIDGE02_RUST_SLICE template @@ -218,6 +200,24 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE02_RUST_VEC +#define CXXBRIDGE02_RUST_VEC +template +class Vec final { +public: + size_t size() const noexcept; + +private: + Vec() noexcept; + Vec(const Vec &other) noexcept; + Vec &operator=(Vec other) noexcept; + void drop() noexcept; + + // Size and alignment statically verified by rust_vec.rs. + std::array repr; +}; +#endif // CXXBRIDGE02_RUST_VEC + #ifndef CXXBRIDGE02_RUST_FN #define CXXBRIDGE02_RUST_FN template From 465305bdb4d05291460d42574c85e0f2c42042aa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 409/2232] Add rust::Vec::empty --- diff --git a/include/cxx.h b/include/cxx.h index a96e6fa..250eba2 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -206,6 +206,7 @@ template class Vec final { public: size_t size() const noexcept; + bool empty() const noexcept { return size() == 0; } private: Vec() noexcept; From e1dcdf731718e1012eb45d30322b2b0e55114233 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 410/2232] Parse vector's type as CxxVector --- diff --git a/syntax/parse.rs b/syntax/parse.rs index f32894b..d14806d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,7 +300,7 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } - } else if ident == "Vector" && generic.args.len() == 1 { + } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; return Ok(Type::CxxVector(Box::new(Ty1 { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index da0040a..0d904fc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -27,9 +27,9 @@ pub mod ffi { fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; - fn c_return_unique_ptr_vector_u8() -> UniquePtr>; - fn c_return_unique_ptr_vector_f64() -> UniquePtr>; - fn c_return_unique_ptr_vector_shared() -> UniquePtr>; + fn c_return_unique_ptr_vector_u8() -> UniquePtr>; + fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -41,9 +41,9 @@ pub mod ffi { fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); - fn c_take_unique_ptr_vector_u8(s: UniquePtr>); - fn c_take_unique_ptr_vector_f64(s: UniquePtr>); - fn c_take_unique_ptr_vector_shared(s: UniquePtr>); + fn c_take_unique_ptr_vector_u8(s: UniquePtr>); + fn c_take_unique_ptr_vector_f64(s: UniquePtr>); + fn c_take_unique_ptr_vector_shared(s: UniquePtr>); fn c_take_vec_u8(v: &Vec); fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); From 3a4299d0db6e1ec6ac9b68a1489ff36ecf314c93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 411/2232] Emit prefixed path when using CxxVector type --- diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 7423f8e..2962a2b 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -35,8 +35,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { - // Do not add cxx namespace to Vector since we're defining it in the user crate - if self.name == "UniquePtr" || self.name == "RustVec" { + if let "UniquePtr" | "RustVec" | "CxxVector" = self.name.to_string().as_str() { let span = self.name.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } From 4074ad22d2a3c5a3e107416a51dfa90c9393ce9f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 412/2232] Prefer where-clause over trait bounds in CxxVector Where-clauses show up less distractingly in rustdocs. --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index c30dd02..b47ec41 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -13,7 +13,10 @@ pub struct CxxVector { _private: [T; 0], } -impl CxxVector { +impl CxxVector +where + T: VectorElement, +{ /// Returns the number of elements in the vector. /// /// Matches the behavior of C++ [std::vector\::size][size]. @@ -67,7 +70,10 @@ pub struct Iter<'a, T> { index: usize, } -impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { +impl<'a, T> IntoIterator for &'a CxxVector +where + T: VectorElement, +{ type Item = &'a T; type IntoIter = Iter<'a, T>; @@ -76,7 +82,10 @@ impl<'a, T: VectorElement> IntoIterator for &'a CxxVector { } } -impl<'a, T: VectorElement> Iterator for Iter<'a, T> { +impl<'a, T> Iterator for Iter<'a, T> +where + T: VectorElement, +{ type Item = &'a T; fn next(&mut self) -> Option { From 4ef2743b295be5132916239555fd97525e29f216 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:13 +0000 Subject: [PATCH 413/2232] Keep type of namespace-iterator in helpers --- diff --git a/gen/write.rs b/gen/write.rs index faf3311..153b5b3 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -978,9 +978,8 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { } fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); + let inner = ty.to_typename(&out.namespace); + let instance = ty.to_mangled(&out.namespace); writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); @@ -1018,9 +1017,8 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { } fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); + let inner = ty.to_typename(&out.namespace); + let instance = ty.to_mangled(&out.namespace); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); @@ -1039,9 +1037,8 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { out.include.utility = true; - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); + let inner = ty.to_typename(&out.namespace); + let instance = ty.to_mangled(&out.namespace); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9b17b11..d1aa638 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -548,7 +548,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; + let mangled = ty.to_mangled(namespace) + "$"; let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -573,9 +573,9 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre } fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { - let name = ty.to_typename(&namespace.segments); + let name = ty.to_typename(namespace); let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; + let mangled = ty.to_mangled(namespace) + "$"; let prefix = format!("cxxbridge02$unique_ptr${}", mangled); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); @@ -648,7 +648,7 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; + let mangled = ty.to_mangled(namespace) + "$"; let prefix = format!("cxxbridge02$std$vector${}", mangled); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); diff --git a/syntax/mangled.rs b/syntax/mangled.rs index 2a2fa4c..a89060d 100644 --- a/syntax/mangled.rs +++ b/syntax/mangled.rs @@ -1,11 +1,12 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::{Atom, Type}; pub trait ToMangled { - fn to_mangled(&self, namespace: &Vec) -> String; + fn to_mangled(&self, namespace: &Namespace) -> String; } impl ToMangled for Type { - fn to_mangled(&self, namespace: &Vec) -> String { + fn to_mangled(&self, namespace: &Namespace) -> String { match self { Type::Ident(ident) => { let mut instance = String::new(); diff --git a/syntax/typename.rs b/syntax/typename.rs index 016749a..b4261f9 100644 --- a/syntax/typename.rs +++ b/syntax/typename.rs @@ -1,11 +1,12 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::{Atom, Type}; pub trait ToTypename { - fn to_typename(&self, namespace: &Vec) -> String; + fn to_typename(&self, namespace: &Namespace) -> String; } impl ToTypename for Type { - fn to_typename(&self, namespace: &Vec) -> String { + fn to_typename(&self, namespace: &Namespace) -> String { match self { Type::Ident(ident) => { let mut inner = String::new(); From 42a7742bcb9c0f1f9a36d4e4e43b1c91df5970d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 414/2232] Revert visibility of Namespace segments from PR 67 --- diff --git a/syntax/namespace.rs b/syntax/namespace.rs index a4e972b..d26bb9e 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -11,7 +11,7 @@ mod kw { #[derive(Clone)] pub struct Namespace { - pub segments: Vec, + segments: Vec, } impl Namespace { From 4c4b550aede3ef0a38167b03061efbee796a0ca6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 415/2232] Eliminate unnecessary ToTypename trait --- diff --git a/gen/write.rs b/gen/write.rs index 153b5b3..42352d0 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -4,7 +4,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::ToTypename; +use crate::syntax::typename::to_typename; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -978,7 +978,7 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { } fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { - let inner = ty.to_typename(&out.namespace); + let inner = to_typename(&out.namespace, ty); let instance = ty.to_mangled(&out.namespace); writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); @@ -1017,7 +1017,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { } fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { - let inner = ty.to_typename(&out.namespace); + let inner = to_typename(&out.namespace, ty); let instance = ty.to_mangled(&out.namespace); writeln!(out, "template <>"); @@ -1037,7 +1037,7 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { out.include.utility = true; - let inner = ty.to_typename(&out.namespace); + let inner = to_typename(&out.namespace, ty); let instance = ty.to_mangled(&out.namespace); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d1aa638..720680d 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -2,7 +2,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::ToTypename; +use crate::syntax::typename::to_typename; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; @@ -573,7 +573,7 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre } fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { - let name = ty.to_typename(namespace); + let name = to_typename(namespace, ty); let inner = ty; let mangled = ty.to_mangled(namespace) + "$"; let prefix = format!("cxxbridge02$unique_ptr${}", mangled); diff --git a/syntax/typename.rs b/syntax/typename.rs index b4261f9..9732d32 100644 --- a/syntax/typename.rs +++ b/syntax/typename.rs @@ -1,37 +1,31 @@ use crate::syntax::namespace::Namespace; use crate::syntax::{Atom, Type}; -pub trait ToTypename { - fn to_typename(&self, namespace: &Namespace) -> String; -} - -impl ToTypename for Type { - fn to_typename(&self, namespace: &Namespace) -> String { - match self { - Type::Ident(ident) => { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - inner += name; - inner += "::"; - } +pub fn to_typename(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + inner += name; + inner += "::"; } - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - inner } - Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)), - Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)), - Type::UniquePtr(ptr) => { - format!("::std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", ptr.inner.to_typename(namespace)), - _ => unimplemented!(), + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + inner + } + Type::RustBox(ptr) => format!("rust_box<{}>", to_typename(namespace, &ptr.inner)), + Type::RustVec(ptr) => format!("rust_vec<{}>", to_typename(namespace, &ptr.inner)), + Type::UniquePtr(ptr) => { + format!("::std::unique_ptr<{}>", to_typename(namespace, &ptr.inner)) } + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + _ => unimplemented!(), } } From f12e983d8c6c2147f8cccf20d0f34b588472c123 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 416/2232] Eliminate unnecessary ToMangled trait --- diff --git a/gen/write.rs b/gen/write.rs index 42352d0..150eb76 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,7 +1,7 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::ToMangled; +use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::typename::to_typename; @@ -979,7 +979,7 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { let inner = to_typename(&out.namespace, ty); - let instance = ty.to_mangled(&out.namespace); + let instance = to_mangled(&out.namespace, ty); writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); @@ -1018,7 +1018,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { let inner = to_typename(&out.namespace, ty); - let instance = ty.to_mangled(&out.namespace); + let instance = to_mangled(&out.namespace, ty); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); @@ -1038,7 +1038,7 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { out.include.utility = true; let inner = to_typename(&out.namespace, ty); - let instance = ty.to_mangled(&out.namespace); + let instance = to_mangled(&out.namespace, ty); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 720680d..8133e6d 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::ToMangled; +use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::typename::to_typename; @@ -548,7 +548,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { let inner = ty; - let mangled = ty.to_mangled(namespace) + "$"; + let mangled = to_mangled(namespace, ty) + "$"; let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -575,7 +575,7 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { let name = to_typename(namespace, ty); let inner = ty; - let mangled = ty.to_mangled(namespace) + "$"; + let mangled = to_mangled(namespace, ty) + "$"; let prefix = format!("cxxbridge02$unique_ptr${}", mangled); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); @@ -648,7 +648,7 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { let inner = ty; - let mangled = ty.to_mangled(namespace) + "$"; + let mangled = to_mangled(namespace, ty) + "$"; let prefix = format!("cxxbridge02$std$vector${}", mangled); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); diff --git a/syntax/mangled.rs b/syntax/mangled.rs index a89060d..f52aa43 100644 --- a/syntax/mangled.rs +++ b/syntax/mangled.rs @@ -1,31 +1,25 @@ use crate::syntax::namespace::Namespace; use crate::syntax::{Atom, Type}; -pub trait ToMangled { - fn to_mangled(&self, namespace: &Namespace) -> String; -} - -impl ToMangled for Type { - fn to_mangled(&self, namespace: &Namespace) -> String { - match self { - Type::Ident(ident) => { - let mut instance = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - instance += name; - instance += "$"; - } +pub fn to_mangled(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut instance = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + instance += name; + instance += "$"; } - instance += &ident.to_string(); - instance } - Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)), - Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)), - Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)), - Type::CxxVector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), - _ => unimplemented!(), + instance += &ident.to_string(); + instance } + Type::RustBox(ptr) => format!("rust_box${}", to_mangled(namespace, &ptr.inner)), + Type::RustVec(ptr) => format!("rust_vec${}", to_mangled(namespace, &ptr.inner)), + Type::UniquePtr(ptr) => format!("std$unique_ptr${}", to_mangled(namespace, &ptr.inner)), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + _ => unimplemented!(), } } From 1763110fdab55a7bcdefd49a474ca8c22a26762b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 417/2232] Clean up VectorElement rust codegen --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 8133e6d..96be928 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -80,9 +80,10 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - // Generate code for CxxVector if T is not an atom - // Code for atoms is already generated - expanded.extend(expand_vector(namespace, &ptr.inner)); + // Generate impl for CxxVector if T is a struct or opaque + // C++ type. Impl for primitives is already provided by cxx + // crate. + expanded.extend(expand_cxx_vector(namespace, ident)); } } } @@ -646,34 +647,32 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt } } -fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { - let inner = ty; - let mangled = to_mangled(namespace, ty) + "$"; - let prefix = format!("cxxbridge02$std$vector${}", mangled); +fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { + let prefix = format!("cxxbridge02$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); quote! { - unsafe impl ::cxx::private::VectorElement for #inner { - fn __vector_size(v: &::cxx::CxxVector<#inner>) -> usize { + unsafe impl ::cxx::private::VectorElement for #elem { + fn __vector_size(v: &::cxx::CxxVector) -> usize { extern "C" { #[link_name = #link_size] - fn __vector_size(_: &::cxx::CxxVector<#inner>) -> usize; + fn __vector_size(_: &::cxx::CxxVector<#elem>) -> usize; } unsafe { __vector_size(v) } } - unsafe fn __get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner { + unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> &Self { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> *const #inner; + fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem; } &*__get_unchecked(v, pos) } - fn __push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) { + fn __push_back(v: &::cxx::CxxVector, item: &Self) { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner); + fn __push_back(_: &::cxx::CxxVector<#elem>, _: &#elem); } unsafe { __push_back(v, item) } } From 3b40b6f8f03478e1fdf4448a813ab50dbd31e4a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 418/2232] Move unique_ptr> implementation into cxx crate --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 96be928..81cf025 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -2,7 +2,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::to_typename; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; @@ -62,19 +61,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); - } - } else if let Type::CxxVector(_) = &ptr.inner { - // Generate code for unique_ptr> if T is not an atom - // or if T is a primitive. - // Code for primitives is already generated - match Atom::from(ident) { - None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)), - Some(atom) => { - if atom.is_valid_vector_target() { - expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); - } - } + expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { @@ -573,11 +560,9 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre } } -fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { - let name = to_typename(namespace, ty); - let inner = ty; - let mangled = to_mangled(namespace, ty) + "$"; - let prefix = format!("cxxbridge02$unique_ptr${}", mangled); +fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { + let name = ident.to_string(); + let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -585,8 +570,8 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = match ty { - Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! { + let new_method = if types.structs.contains_key(ident) { + Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] @@ -596,13 +581,14 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe { __new(&mut repr, &mut value) } repr } - }), - _ => None, + }) + } else { + None }; quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #inner { - const __NAME: &'static str = #name; + unsafe impl ::cxx::private::UniquePtrTarget for #ident { + const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_null] @@ -616,7 +602,7 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] - fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner); + fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident); } let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); __raw(&mut repr, raw); @@ -625,14 +611,14 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self { extern "C" { #[link_name = #link_get] - fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner; + fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident; } __get(&repr) } unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self { extern "C" { #[link_name = #link_release] - fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner; + fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident; } __release(&mut repr) } @@ -648,13 +634,21 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt } fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { + let name = elem.to_string(); let prefix = format!("cxxbridge02$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); + let unique_ptr_prefix = format!("cxxbridge02$unique_ptr$std$vector${}{}$", namespace, elem); + let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); + let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); + let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); + let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); + let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); quote! { unsafe impl ::cxx::private::VectorElement for #elem { + const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __vector_size(v: &::cxx::CxxVector) -> usize { extern "C" { #[link_name = #link_size] @@ -676,6 +670,45 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { } unsafe { __push_back(v, item) } } + fn __unique_ptr_null() -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_unique_ptr_null] + fn __unique_ptr_null(this: *mut *mut ::std::ffi::c_void); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __unique_ptr_null(&mut repr) } + repr + } + unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector) -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_unique_ptr_raw] + fn __unique_ptr_raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem>); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + __unique_ptr_raw(&mut repr, raw); + repr + } + unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector { + extern "C" { + #[link_name = #link_unique_ptr_get] + fn __unique_ptr_get(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem>; + } + __unique_ptr_get(&repr) + } + unsafe fn __unique_ptr_release(mut repr: *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector { + extern "C" { + #[link_name = #link_unique_ptr_release] + fn __unique_ptr_release(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem>; + } + __unique_ptr_release(&mut repr) + } + unsafe fn __unique_ptr_drop(mut repr: *mut ::std::ffi::c_void) { + extern "C" { + #[link_name = #link_unique_ptr_drop] + fn __unique_ptr_drop(this: *mut *mut ::std::ffi::c_void); + } + __unique_ptr_drop(&mut repr); + } } } } diff --git a/src/cxx.cc b/src/cxx.cc index b9e4826..aacea9c 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -212,20 +212,10 @@ void cxxbridge02$unique_ptr$std$string$drop( std::vector &s, const CXX_TYPE &item) noexcept { \ s.push_back(item); \ } \ - static_assert( \ - sizeof(std::unique_ptr>) == sizeof(void *), ""); \ - static_assert( \ - alignof(std::unique_ptr>) == alignof(void *), ""); \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ } \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new( \ - std::unique_ptr> *ptr, \ - std::vector *value) noexcept { \ - new (ptr) std::unique_ptr>( \ - new std::vector(std::move(*value))); \ - } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index b47ec41..d9e8892 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,4 +1,8 @@ +use std::ffi::c_void; +use std::fmt::{self, Display}; +use std::marker::PhantomData; use std::mem; +use std::ptr; /// Binding to C++ `std::vector>`. /// @@ -94,18 +98,46 @@ where } } +pub struct TypeName { + element: PhantomData, +} + +impl TypeName { + pub const fn new() -> Self { + TypeName { + element: PhantomData, + } + } +} + +impl Display for TypeName +where + T: VectorElement, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CxxVector<{}>", T::__NAME) + } +} + // Methods are private; not intended to be implemented outside of cxxbridge // codebase. #[doc(hidden)] pub unsafe trait VectorElement: Sized { + const __NAME: &'static dyn Display; fn __vector_size(v: &CxxVector) -> usize; unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; fn __push_back(v: &CxxVector, item: &Self); + fn __unique_ptr_null() -> *mut c_void; + unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; + unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; + unsafe fn __unique_ptr_release(repr: *mut c_void) -> *mut CxxVector; + unsafe fn __unique_ptr_drop(repr: *mut c_void); } macro_rules! impl_vector_element_for_primitive { ($ty:ident) => { unsafe impl VectorElement for $ty { + const __NAME: &'static dyn Display = &stringify!($ty); fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { @@ -133,6 +165,55 @@ macro_rules! impl_vector_element_for_primitive { } unsafe { __push_back(v, item) } } + fn __unique_ptr_null() -> *mut c_void { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$null")] + fn __unique_ptr_null(this: *mut *mut c_void); + } + } + let mut repr = ptr::null_mut::(); + unsafe { __unique_ptr_null(&mut repr) } + repr + } + unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$raw")] + fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); + } + } + let mut repr = ptr::null_mut::(); + __unique_ptr_raw(&mut repr, raw); + repr + } + unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$get")] + fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; + } + } + __unique_ptr_get(&repr) + } + unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$release")] + fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; + } + } + __unique_ptr_release(&mut repr) + } + unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$drop")] + fn __unique_ptr_drop(this: *mut *mut c_void); + } + } + __unique_ptr_drop(&mut repr); + } } }; } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index b50e870..98b4b7d 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,4 +1,5 @@ use crate::cxx_string::CxxString; +use crate::cxx_vector::{self, CxxVector, VectorElement}; use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::marker::PhantomData; @@ -149,7 +150,7 @@ where // codebase. pub unsafe trait UniquePtrTarget { #[doc(hidden)] - const __NAME: &'static str; + const __NAME: &'static dyn Display; #[doc(hidden)] fn __null() -> *mut c_void; #[doc(hidden)] @@ -186,7 +187,7 @@ extern "C" { } unsafe impl UniquePtrTarget for CxxString { - const __NAME: &'static str = "CxxString"; + const __NAME: &'static dyn Display = &"CxxString"; fn __null() -> *mut c_void { let mut repr = ptr::null_mut::(); unsafe { unique_ptr_std_string_null(&mut repr) } @@ -207,3 +208,25 @@ unsafe impl UniquePtrTarget for CxxString { unique_ptr_std_string_drop(&mut repr); } } + +unsafe impl UniquePtrTarget for CxxVector +where + T: VectorElement + 'static, +{ + const __NAME: &'static dyn Display = &cxx_vector::TypeName::::new(); + fn __null() -> *mut c_void { + T::__unique_ptr_null() + } + unsafe fn __raw(raw: *mut Self) -> *mut c_void { + T::__unique_ptr_raw(raw) + } + unsafe fn __get(repr: *mut c_void) -> *const Self { + T::__unique_ptr_get(repr) + } + unsafe fn __release(repr: *mut c_void) -> *mut Self { + T::__unique_ptr_release(repr) + } + unsafe fn __drop(repr: *mut c_void) { + T::__unique_ptr_drop(repr); + } +} From 2eca4a0ce9ccff8ff14a542c5770bf7cdc406418 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 419/2232] Move C++-specific to_typename to C++ code generator --- diff --git a/gen/write.rs b/gen/write.rs index 150eb76..f8bd1d8 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -4,7 +4,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::to_typename; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -889,6 +888,35 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { } } +fn to_typename(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut inner = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + inner += name; + inner += "::"; + } + } + if let Some(ti) = Atom::from(ident) { + inner += ti.to_cxx(); + } else { + inner += &ident.to_string(); + }; + inner + } + Type::RustBox(ptr) => format!("rust_box<{}>", to_typename(namespace, &ptr.inner)), + Type::RustVec(ptr) => format!("rust_vec<{}>", to_typename(namespace, &ptr.inner)), + Type::UniquePtr(ptr) => { + format!("::std::unique_ptr<{}>", to_typename(namespace, &ptr.inner)) + } + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + _ => unimplemented!(), + } +} + fn write_generic_instantiations(out: &mut OutFile, types: &Types) { fn allow_unique_ptr(ident: &Ident) -> bool { Atom::from(ident).is_none() diff --git a/syntax/mod.rs b/syntax/mod.rs index c992690..d5b90f0 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -14,7 +14,6 @@ mod parse; pub mod set; pub mod symbol; mod tokens; -pub mod typename; pub mod types; use self::parse::kw; diff --git a/syntax/typename.rs b/syntax/typename.rs deleted file mode 100644 index 9732d32..0000000 --- a/syntax/typename.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::syntax::namespace::Namespace; -use crate::syntax::{Atom, Type}; - -pub fn to_typename(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(ident) => { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - inner += name; - inner += "::"; - } - } - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - inner - } - Type::RustBox(ptr) => format!("rust_box<{}>", to_typename(namespace, &ptr.inner)), - Type::RustVec(ptr) => format!("rust_vec<{}>", to_typename(namespace, &ptr.inner)), - Type::UniquePtr(ptr) => { - format!("::std::unique_ptr<{}>", to_typename(namespace, &ptr.inner)) - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), - _ => unimplemented!(), - } -} From 0d3a7357fd59d126d147196775a9db0879e7af3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 420/2232] Remove unimplemented constructors from rust::Vec header --- diff --git a/include/cxx.h b/include/cxx.h index 250eba2..5f369d4 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -209,9 +209,6 @@ public: bool empty() const noexcept { return size() == 0; } private: - Vec() noexcept; - Vec(const Vec &other) noexcept; - Vec &operator=(Vec other) noexcept; void drop() noexcept; // Size and alignment statically verified by rust_vec.rs. From cb80057051e15ff0384497c064f4bc596412103f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:14 +0000 Subject: [PATCH 421/2232] Wire up Vec destructor --- diff --git a/include/cxx.h b/include/cxx.h index 5f369d4..d3721ea 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -205,6 +205,8 @@ private: template class Vec final { public: + ~Vec() noexcept { this->drop(); } + size_t size() const noexcept; bool empty() const noexcept { return size() == 0; } From 219c0790d4e157ca358d5a7baa08972ab6fcd4af Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 422/2232] Generate data accessor for Vec --- diff --git a/gen/write.rs b/gen/write.rs index f8bd1d8..9192a7e 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1021,6 +1021,11 @@ fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); + writeln!( + out, + "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + inner, instance, + ); writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); } @@ -1061,6 +1066,15 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$data(this);", + instance, + ); + writeln!(out, "}}"); } fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { diff --git a/include/cxx.h b/include/cxx.h index d3721ea..c3037bd 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -209,6 +209,7 @@ public: size_t size() const noexcept; bool empty() const noexcept { return size() == 0; } + const T *data() const noexcept; private: void drop() noexcept; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 81cf025..15351f0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -540,10 +540,12 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); + let link_data = format!("{}data", link_prefix); let local_prefix = format_ident!("{}__vec_", ident); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); + let local_data = format_ident!("{}data", local_prefix); let span = ty.span(); quote_spanned! {span=> @@ -557,6 +559,11 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#inner>) -> usize { (*this).len() } + #[doc(hidden)] + #[export_name = #link_data] + unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#inner>) -> *const #inner { + (*this).as_ptr() + } } } diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 1c0c64f..32db8cc 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -29,6 +29,10 @@ impl RustVec { pub fn len(&self) -> usize { self.repr.len() } + + pub fn as_ptr(&self) -> *const T { + self.repr.as_ptr() + } } const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); From 503d01902a6cd9b0f2dfd81f2b39c361144c06da Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 423/2232] Add stride accessor for Vec Will be required for implementing an iterator. --- diff --git a/gen/write.rs b/gen/write.rs index 9192a7e..d963bb9 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1026,6 +1026,11 @@ fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", inner, instance, ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$stride() noexcept;", + instance, + ); writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); } @@ -1075,6 +1080,11 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { instance, ); writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); + writeln!(out, "}}"); } fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { diff --git a/include/cxx.h b/include/cxx.h index c3037bd..f40614b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -212,6 +212,7 @@ public: const T *data() const noexcept; private: + static size_t stride() noexcept; void drop() noexcept; // Size and alignment statically verified by rust_vec.rs. diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 15351f0..7503f3a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -541,11 +541,13 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_data = format!("{}data", link_prefix); + let link_stride = format!("{}stride", link_prefix); let local_prefix = format_ident!("{}__vec_", ident); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); let local_data = format_ident!("{}data", local_prefix); + let local_stride = format_ident!("{}stride", local_prefix); let span = ty.span(); quote_spanned! {span=> @@ -564,6 +566,11 @@ fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStre unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#inner>) -> *const #inner { (*this).as_ptr() } + #[doc(hidden)] + #[export_name = #link_stride] + unsafe extern "C" fn #local_stride() -> usize { + ::std::mem::size_of::<#inner>() + } } } From c87c215f56183fca32b2d82d4739043c72a3d10a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 424/2232] Add begin/end iterators to rust::Vec --- diff --git a/gen/write.rs b/gen/write.rs index d963bb9..f67057b 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -147,6 +147,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } Type::RustVec(_) => { out.include.array = true; + out.include.type_traits = true; needs_rust_vec = true; } Type::Str(_) => { diff --git a/include/cxx.h b/include/cxx.h index f40614b..e16d9e0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -205,12 +205,50 @@ private: template class Vec final { public: + using value_type = T; + ~Vec() noexcept { this->drop(); } size_t size() const noexcept; bool empty() const noexcept { return size() == 0; } const T *data() const noexcept; + class const_iterator { + public: + using value_type = typename std::add_const::type; + using reference = typename std::add_lvalue_reference< + typename std::add_const::type>::type; + + const T &operator*() const { return *static_cast(this->pos); } + const_iterator &operator++() { + this->pos = static_cast(this->pos) + this->stride; + return *this; + } + bool operator==(const const_iterator &other) const { + return this->pos == other.pos; + } + bool operator!=(const const_iterator &other) const { + return this->pos != other.pos; + } + + private: + friend class Vec; + const void *pos; + size_t stride; + }; + + const_iterator begin() const noexcept { + const_iterator it; + it.pos = this->data(); + it.stride = this->stride(); + return it; + } + const_iterator end() const noexcept { + const_iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; + } + private: static size_t stride() noexcept; void drop() noexcept; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 46f04be..502be05 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -163,17 +163,15 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { } void c_take_vec_u8(const rust::Vec &v) { - auto cv = static_cast>(v); - uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); + uint8_t sum = std::accumulate(v.begin(), v.end(), 0); if (sum == 200) { cxx_test_suite_set_correct(); } } void c_take_vec_shared(const rust::Vec &v) { - auto cv = static_cast>(v); uint32_t sum = 0; - for (auto i : cv) { + for (auto i : v) { sum += i.z; } if (sum == 2021) { From 63da4d37b96995083a23ec710f3641a51cc312ed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 425/2232] Emit unique_ptr> as part of write_vector --- diff --git a/gen/write.rs b/gen/write.rs index f67057b..f456e18 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -923,11 +923,6 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { Atom::from(ident).is_none() } - fn allow_vector(ident: &Ident) -> bool { - // Note: built-in types such as u8 are already defined in cxx.cc - Atom::from(ident).is_none() - } - out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -944,21 +939,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, &ptr.inner, types); - } - } else if let Type::CxxVector(ptr1) = &ptr.inner { - if let Type::Ident(inner) = &ptr1.inner { - if allow_vector(inner) { - out.next_section(); - write_unique_ptr(out, &ptr.inner, types); - } + write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if allow_vector(inner) { + if Atom::from(inner).is_none() { out.next_section(); - write_vector(out, inner); + write_vector(out, ty, inner, types); } } } @@ -1088,13 +1076,29 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { + let ty = Type::Ident(ident.clone()); + let instance = to_mangled(&out.namespace, &ty); + + writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + + write_unique_ptr_common(out, &ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); +} + +// Shared by UniquePtr and UniquePtr>. +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { out.include.utility = true; let inner = to_typename(&out.namespace, ty); let instance = to_mangled(&out.namespace, ty); - writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + let can_construct_from_value = match ty { + Type::Ident(ident) => types.structs.contains_key(ident), + _ => false, + }; + writeln!( out, "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", @@ -1112,21 +1116,18 @@ fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - match ty { - Type::Ident(ident) if types.structs.contains_key(ident) => { - writeln!( + if can_construct_from_value { + writeln!( out, "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); - } - _ => (), + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); } writeln!( out, @@ -1156,30 +1157,15 @@ fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!(out, " ptr->~unique_ptr();"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); } -fn write_vector(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in &out.namespace { - inner += name; - inner += "::"; - } - } - let mut instance = inner.clone(); - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - instance += &ident.to_string(); - let instance = instance.replace("::", "$"); +fn write_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE02_vector_{}", instance); - writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); writeln!( out, "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", @@ -1201,5 +1187,8 @@ fn write_vector(out: &mut OutFile, ident: &Ident) { ); writeln!(out, " s.push_back(item);"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); + + write_unique_ptr_common(out, vector_ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); } From cd08c440ce1b2e3daff4dcc748ab15835ae2a4ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 426/2232] Pare down to_typename to match usage --- diff --git a/gen/write.rs b/gen/write.rs index f456e18..19762cc 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -889,32 +889,21 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { } } +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. fn to_typename(namespace: &Namespace, ty: &Type) -> String { match ty { Type::Ident(ident) => { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - inner += name; - inner += "::"; - } + let mut path = String::new(); + for name in namespace { + path += name; + path += "::"; } - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - inner - } - Type::RustBox(ptr) => format!("rust_box<{}>", to_typename(namespace, &ptr.inner)), - Type::RustVec(ptr) => format!("rust_vec<{}>", to_typename(namespace, &ptr.inner)), - Type::UniquePtr(ptr) => { - format!("::std::unique_ptr<{}>", to_typename(namespace, &ptr.inner)) + path += &ident.to_string(); + path } Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), - _ => unimplemented!(), + _ => unreachable!(), } } From 029f1d6a2efeffb4cef169620a14b17ac80b64c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 427/2232] Move C++-specific type printing back into C++ generator --- diff --git a/gen/write.rs b/gen/write.rs index 19762cc..c21cda1 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -810,7 +810,21 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(a) => write!(out, "{}", a.to_cxx()), + Some(Bool) => write!(out, "bool"), + Some(U8) => write!(out, "uint8_t"), + Some(U16) => write!(out, "uint16_t"), + Some(U32) => write!(out, "uint32_t"), + Some(U64) => write!(out, "uint64_t"), + Some(Usize) => write!(out, "size_t"), + Some(I8) => write!(out, "int8_t"), + Some(I16) => write!(out, "int16_t"), + Some(I32) => write!(out, "int32_t"), + Some(I64) => write!(out, "int64_t"), + Some(Isize) => write!(out, "::rust::isize"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), + Some(CxxString) => write!(out, "::std::string"), + Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { diff --git a/syntax/atom.rs b/syntax/atom.rs index c68b3fe..c94abe2 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -43,27 +43,6 @@ impl Atom { } } - pub fn to_cxx(&self) -> &'static str { - use self::Atom::*; - match self { - Bool => "bool", - U8 => "uint8_t", - U16 => "uint16_t", - U32 => "uint32_t", - U64 => "uint64_t", - Usize => "size_t", - I8 => "int8_t", - I16 => "int16_t", - I32 => "int32_t", - I64 => "int64_t", - Isize => "::rust::isize", - F32 => "float", - F64 => "double", - CxxString => "::std::string", - RustString => "::rust::String", - } - } - pub fn is_valid_vector_target(&self) -> bool { use self::Atom::*; *self == U8 From ce82c99f951607fc5ec5f22a841be6b14690fac8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 428/2232] Remove redundant semicolons from vector ops in cxx.cc --- diff --git a/src/cxx.cc b/src/cxx.cc index aacea9c..4351eda 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -237,16 +237,16 @@ void cxxbridge02$unique_ptr$std$string$drop( } extern "C" { -STD_VECTOR_OPS(u8, uint8_t); -STD_VECTOR_OPS(u16, uint16_t); -STD_VECTOR_OPS(u32, uint32_t); -STD_VECTOR_OPS(u64, uint64_t); -STD_VECTOR_OPS(usize, size_t); -STD_VECTOR_OPS(i8, int8_t); -STD_VECTOR_OPS(i16, int16_t); -STD_VECTOR_OPS(i32, int32_t); -STD_VECTOR_OPS(i64, int64_t); -STD_VECTOR_OPS(isize, rust::isize); -STD_VECTOR_OPS(f32, float); -STD_VECTOR_OPS(f64, double); +STD_VECTOR_OPS(u8, uint8_t) +STD_VECTOR_OPS(u16, uint16_t) +STD_VECTOR_OPS(u32, uint32_t) +STD_VECTOR_OPS(u64, uint64_t) +STD_VECTOR_OPS(usize, size_t) +STD_VECTOR_OPS(i8, int8_t) +STD_VECTOR_OPS(i16, int16_t) +STD_VECTOR_OPS(i32, int32_t) +STD_VECTOR_OPS(i64, int64_t) +STD_VECTOR_OPS(isize, rust::isize) +STD_VECTOR_OPS(f32, float) +STD_VECTOR_OPS(f64, double) } // extern "C" From 6787be69587b3443bf54fbf46d47b4f98cff9852 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:15 +0000 Subject: [PATCH 429/2232] Move primitive Vec C++ shims into cxx crate --- diff --git a/gen/write.rs b/gen/write.rs index c21cda1..94de4fb 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -934,9 +934,11 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { write_rust_box_extern(out, inner); } } else if let Type::RustVec(ty) = ty { - if let Type::Ident(_) = &ty.inner { - out.next_section(); - write_rust_vec_extern(out, &ty.inner); + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_rust_vec_extern(out, inner); + } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { @@ -964,8 +966,10 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { write_rust_box_impl(out, inner); } } else if let Type::RustVec(ty) = ty { - if let Type::Ident(_) = &ty.inner { - write_rust_vec_impl(out, &ty.inner); + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + write_rust_vec_impl(out, inner); + } } } } @@ -997,9 +1001,10 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); +fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); @@ -1046,9 +1051,10 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); +fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); diff --git a/src/cxx.cc b/src/cxx.cc index 4351eda..63b2166 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -236,17 +236,54 @@ void cxxbridge02$unique_ptr$std$string$drop( ptr->~unique_ptr(); \ } +#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge02$rust_vec$##RUST_TYPE##$drop( \ + rust::Vec *ptr) noexcept; \ + size_t cxxbridge02$rust_vec$##RUST_TYPE##$len( \ + const rust::Vec *ptr) noexcept; \ + const CXX_TYPE *cxxbridge02$rust_vec$##RUST_TYPE##$data( \ + const rust::Vec *ptr) noexcept; \ + size_t cxxbridge02$rust_vec$##RUST_TYPE##$stride() noexcept; + +#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + void rust::Vec::drop() noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$drop(this); \ + } \ + template <> \ + size_t rust::Vec::size() const noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$len(this); \ + } \ + template <> \ + const CXX_TYPE *rust::Vec::data() const noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$data(this); \ + } \ + template <> \ + size_t rust::Vec::stride() noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$stride(); \ + } + +// Usize and isize are the same type as one of the below. +#define FOR_EACH_SIZED_PRIMITIVE(MACRO) \ + MACRO(u8, uint8_t) \ + MACRO(u16, uint16_t) \ + MACRO(u32, uint32_t) \ + MACRO(u64, uint64_t) \ + MACRO(i8, int8_t) \ + MACRO(i16, int16_t) \ + MACRO(i32, int32_t) \ + MACRO(i64, int64_t) \ + MACRO(f32, float) \ + MACRO(f64, double) + +#define FOR_EACH_PRIMITIVE(MACRO) \ + FOR_EACH_SIZED_PRIMITIVE(MACRO) \ + MACRO(usize, size_t) \ + MACRO(isize, rust::isize) + extern "C" { -STD_VECTOR_OPS(u8, uint8_t) -STD_VECTOR_OPS(u16, uint16_t) -STD_VECTOR_OPS(u32, uint32_t) -STD_VECTOR_OPS(u64, uint64_t) -STD_VECTOR_OPS(usize, size_t) -STD_VECTOR_OPS(i8, int8_t) -STD_VECTOR_OPS(i16, int16_t) -STD_VECTOR_OPS(i32, int32_t) -STD_VECTOR_OPS(i64, int64_t) -STD_VECTOR_OPS(isize, rust::isize) -STD_VECTOR_OPS(f32, float) -STD_VECTOR_OPS(f64, double) +FOR_EACH_PRIMITIVE(STD_VECTOR_OPS) +FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) } // extern "C" + +FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) From 92105da4913bbf91eb28fa54c7d2c993d72f2ff8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 430/2232] Clarify name of C++ codegen for C++ vector --- diff --git a/gen/write.rs b/gen/write.rs index 94de4fb..5898654 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -951,7 +951,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::Ident(inner) = &ptr.inner { if Atom::from(inner).is_none() { out.next_section(); - write_vector(out, ty, inner, types); + write_cxx_vector(out, ty, inner, types); } } } @@ -1168,7 +1168,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { let element = Type::Ident(element.clone()); let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); From a006bca89140585c98c952091e4efbc78098ae51 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 431/2232] Move primitive Vec Rust shims into cxx crate --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 7503f3a..9e0ea9a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -56,7 +56,9 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + if Atom::from(ident).is_none() { + hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 32db8cc..4ca17ca 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,4 +1,5 @@ use std::mem; +use std::ptr; #[repr(C)] pub struct RustVec { @@ -35,5 +36,47 @@ impl RustVec { } } +macro_rules! rust_vec_shims_for_primitive { + ($ty:ident) => { + const _: () = { + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$drop")] + unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { + ptr::drop_in_place(this); + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$len")] + unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { + (*this).len() + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$data")] + unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { + (*this).as_ptr() + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$stride")] + unsafe extern "C" fn __stride() -> usize { + mem::size_of::<$ty>() + } + } + }; + }; +} + +rust_vec_shims_for_primitive!(u8); +rust_vec_shims_for_primitive!(u16); +rust_vec_shims_for_primitive!(u32); +rust_vec_shims_for_primitive!(u64); +rust_vec_shims_for_primitive!(i8); +rust_vec_shims_for_primitive!(i16); +rust_vec_shims_for_primitive!(i32); +rust_vec_shims_for_primitive!(i64); +rust_vec_shims_for_primitive!(f32); +rust_vec_shims_for_primitive!(f64); + const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); const_assert_eq!(mem::align_of::(), mem::align_of::>()); From f044663b2a6e91747a4a127c09d76670bf1455f2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 432/2232] Emit const assertions for every vector element type --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index d9e8892..8294044 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -136,6 +136,8 @@ pub unsafe trait VectorElement: Sized { macro_rules! impl_vector_element_for_primitive { ($ty:ident) => { + const_assert_eq!(1, mem::align_of::>()); + unsafe impl VectorElement for $ty { const __NAME: &'static dyn Display = &stringify!($ty); fn __vector_size(v: &CxxVector<$ty>) -> usize { @@ -230,5 +232,3 @@ impl_vector_element_for_primitive!(i64); impl_vector_element_for_primitive!(isize); impl_vector_element_for_primitive!(f32); impl_vector_element_for_primitive!(f64); - -const_assert_eq!(1, mem::align_of::>()); diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 4ca17ca..564851e 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -38,6 +38,9 @@ impl RustVec { macro_rules! rust_vec_shims_for_primitive { ($ty:ident) => { + const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); + const_assert_eq!(mem::align_of::(), mem::align_of::>()); + const _: () = { attr! { #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$drop")] @@ -77,6 +80,3 @@ rust_vec_shims_for_primitive!(i32); rust_vec_shims_for_primitive!(i64); rust_vec_shims_for_primitive!(f32); rust_vec_shims_for_primitive!(f64); - -const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); -const_assert_eq!(mem::align_of::(), mem::align_of::>()); From 7ff1b8c1b5d03b9eab4c3ffbc13d2ff574d9ae49 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 433/2232] Move is_valid_vector_target into type checker --- diff --git a/syntax/atom.rs b/syntax/atom.rs index c94abe2..eeea831 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -42,22 +42,6 @@ impl Atom { _ => None, } } - - pub fn is_valid_vector_target(&self) -> bool { - use self::Atom::*; - *self == U8 - || *self == U16 - || *self == U32 - || *self == U64 - || *self == Usize - || *self == I8 - || *self == I16 - || *self == I32 - || *self == I64 - || *self == Isize - || *self == F32 - || *self == F64 - } } impl PartialEq for Ident { diff --git a/syntax/check.rs b/syntax/check.rs index cbfa91b..8d4b207 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -92,7 +92,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_vec(cx: &mut Check, ptr: &Ty1) { // Vec can contain either user-defined type or u8 if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).map(|a| a.is_valid_vector_target()) == Some(true) { + if Atom::from(ident).map(is_valid_vector_target) == Some(true) { return; } else if cx.types.cxx.contains(ident) { cx.error(ptr, error::VEC_CXX_TYPE.msg); @@ -130,7 +130,7 @@ fn check_type_vector(cx: &mut Check, ptr: &Ty1) { match Atom::from(ident) { None => return, Some(atom) => { - if atom.is_valid_vector_target() { + if is_valid_vector_target(atom) { return; } } @@ -299,6 +299,21 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) } +fn is_valid_vector_target(atom: Atom) -> bool { + atom == U8 + || atom == U16 + || atom == U32 + || atom == U64 + || atom == Usize + || atom == I8 + || atom == I16 + || atom == I32 + || atom == I64 + || atom == Isize + || atom == F32 + || atom == F64 +} + fn span_for_struct_error(strct: &Struct) -> TokenStream { let struct_token = strct.struct_token; let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); From fff4c8a5d6709d47b4844805c7e15c1e751b8983 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 434/2232] Clarify vector checking function names --- diff --git a/syntax/check.rs b/syntax/check.rs index 8d4b207..5f767c0 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,9 +29,9 @@ fn do_typecheck(cx: &mut Check) { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), - Type::RustVec(ptr) => check_type_vec(cx, ptr), + Type::RustVec(ptr) => check_type_rust_vec(cx, ptr), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), - Type::CxxVector(ptr) => check_type_vector(cx, ptr), + Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Slice(ty) => check_type_slice(cx, ty), _ => {} @@ -89,7 +89,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported target type of Box"); } -fn check_type_vec(cx: &mut Check, ptr: &Ty1) { +fn check_type_rust_vec(cx: &mut Check, ptr: &Ty1) { // Vec can contain either user-defined type or u8 if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).map(is_valid_vector_target) == Some(true) { @@ -121,7 +121,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported unique_ptr target type"); } -fn check_type_vector(cx: &mut Check, ptr: &Ty1) { +fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(ident) { cx.error(ptr, "vector of a Rust type is not supported yet"); From 2410aff870e114e0095803c0d49d4c1029b5141c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 435/2232] Rename vector element checking function Vectors have elements, not targets. Pointers have targets. --- diff --git a/syntax/check.rs b/syntax/check.rs index 5f767c0..b53d0a4 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -92,7 +92,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_rust_vec(cx: &mut Check, ptr: &Ty1) { // Vec can contain either user-defined type or u8 if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).map(is_valid_vector_target) == Some(true) { + if Atom::from(ident).map(is_valid_vector_element) == Some(true) { return; } else if cx.types.cxx.contains(ident) { cx.error(ptr, error::VEC_CXX_TYPE.msg); @@ -130,7 +130,7 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { match Atom::from(ident) { None => return, Some(atom) => { - if is_valid_vector_target(atom) { + if is_valid_vector_element(atom) { return; } } @@ -299,7 +299,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) } -fn is_valid_vector_target(atom: Atom) -> bool { +fn is_valid_vector_element(atom: Atom) -> bool { atom == U8 || atom == U16 || atom == U32 From 76a8424864b3211e3edfa93d1b2bf772e66f07b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 436/2232] Express is_valid_vector_element more compactly --- diff --git a/syntax/check.rs b/syntax/check.rs index b53d0a4..286dbae 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -300,18 +300,10 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { } fn is_valid_vector_element(atom: Atom) -> bool { - atom == U8 - || atom == U16 - || atom == U32 - || atom == U64 - || atom == Usize - || atom == I8 - || atom == I16 - || atom == I32 - || atom == I64 - || atom == Isize - || atom == F32 - || atom == F64 + match atom { + U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => true, + Bool | CxxString | RustString => false, + } } fn span_for_struct_error(strct: &Struct) -> TokenStream { From c6d891ece2a3568acf9f2a081272427d8836d190 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:16 +0000 Subject: [PATCH 437/2232] Fix Vec type checking The previous logic incorrectly accepted Atoms outside the intended set, and also emitted duplicate errors for opaque C++ types. --- diff --git a/syntax/check.rs b/syntax/check.rs index 286dbae..9683224 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,7 +29,7 @@ fn do_typecheck(cx: &mut Check) { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), - Type::RustVec(ptr) => check_type_rust_vec(cx, ptr), + Type::RustVec(ty) => check_type_rust_vec(cx, ty), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), @@ -89,19 +89,21 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported target type of Box"); } -fn check_type_rust_vec(cx: &mut Check, ptr: &Ty1) { - // Vec can contain either user-defined type or u8 - if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).map(is_valid_vector_element) == Some(true) { - return; - } else if cx.types.cxx.contains(ident) { - cx.error(ptr, error::VEC_CXX_TYPE.msg); - } else { +fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { + if let Type::Ident(ident) = &ty.inner { + if cx.types.cxx.contains(ident) { + cx.error(ty, "Rust Vec containing C++ type is not supported yet"); return; } + + match Atom::from(ident) { + Some(atom) if is_valid_vector_element(atom) => return, + None => return, + _ => {} + } } - cx.error(ptr, "unsupported target type of Vec"); + cx.error(ty, "unsupported element type of Vec"); } fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { diff --git a/syntax/error.rs b/syntax/error.rs index 103a54f..f52d651 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -15,7 +15,6 @@ impl Display for Error { pub static ERRORS: &[Error] = &[ BOX_CXX_TYPE, - VEC_CXX_TYPE, CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, @@ -30,12 +29,6 @@ pub static BOX_CXX_TYPE: Error = Error { note: Some("hint: use UniquePtr<>"), }; -pub static VEC_CXX_TYPE: Error = Error { - msg: "Vec of a C++ type is not supported yet", - label: None, - note: Some("hint: use UniquePtr<>"), -}; - pub static CXXBRIDGE_RESERVED: Error = Error { msg: "identifiers starting with cxxbridge are reserved", label: Some("reserved identifier"), From c0faaf6cda0e00f831a681d6b1f2bdd1f4599e9c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 438/2232] Update cxx vector check to match rust vec check --- diff --git a/syntax/check.rs b/syntax/check.rs index 9683224..090a9ae 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -126,18 +126,19 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(ident) { - cx.error(ptr, "vector of a Rust type is not supported yet"); + cx.error( + ptr, + "C++ vector containing a Rust type is not supported yet", + ); } match Atom::from(ident) { + Some(atom) if is_valid_vector_element(atom) => return, None => return, - Some(atom) => { - if is_valid_vector_element(atom) { - return; - } - } + _ => {} } } + cx.error(ptr, "unsupported vector target type"); } From 6bd63de58e2ee6b729bc3863e1406b197c9a4980 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 439/2232] Inline vector element check These will need to diverge shortly; in particular we'd like to support Rust vectors containing Rust strings and C++ vectors containing C++ strings. --- diff --git a/syntax/check.rs b/syntax/check.rs index 090a9ae..fd5ed67 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -97,9 +97,10 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { } match Atom::from(ident) { - Some(atom) if is_valid_vector_element(atom) => return, - None => return, - _ => {} + 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) => return, + Some(Bool) | Some(RustString) => { /* todo */ } + Some(CxxString) => {} } } @@ -133,9 +134,10 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { } match Atom::from(ident) { - Some(atom) if is_valid_vector_element(atom) => return, - None => return, - _ => {} + 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) => return, + Some(CxxString) => { /* todo */ } + Some(Bool) | Some(RustString) => {} } } @@ -302,13 +304,6 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) } -fn is_valid_vector_element(atom: Atom) -> bool { - match atom { - U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => true, - Bool | CxxString | RustString => false, - } -} - fn span_for_struct_error(strct: &Struct) -> TokenStream { let struct_token = strct.struct_token; let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); From 83a7ec99014b7bdce2ea1218e97ddf1b9f128855 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 440/2232] Remove unnecessary use of to_mangled from Rust generator --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9e0ea9a..e3ab530 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,4 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -7,7 +6,7 @@ use crate::syntax::{ }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token}; +use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -57,7 +56,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { if Atom::from(ident).is_none() { - hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + hidden.extend(expand_rust_vec(namespace, ident)); } } } else if let Type::UniquePtr(ptr) = ty { @@ -536,42 +535,40 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { - let inner = ty; - let mangled = to_mangled(namespace, ty) + "$"; - let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); +fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { + let link_prefix = format!("cxxbridge02$rust_vec${}{}$", namespace, elem); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_data = format!("{}data", link_prefix); let link_stride = format!("{}stride", link_prefix); - let local_prefix = format_ident!("{}__vec_", ident); + let local_prefix = format_ident!("{}__vec_", elem); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); let local_data = format_ident!("{}data", local_prefix); let local_stride = format_ident!("{}stride", local_prefix); - let span = ty.span(); + let span = elem.span(); quote_spanned! {span=> #[doc(hidden)] #[export_name = #link_drop] - unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#inner>) { + unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) { ::std::ptr::drop_in_place(this); } #[doc(hidden)] #[export_name = #link_len] - unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#inner>) -> usize { + unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#elem>) -> usize { (*this).len() } #[doc(hidden)] #[export_name = #link_data] - unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#inner>) -> *const #inner { + unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#elem>) -> *const #elem { (*this).as_ptr() } #[doc(hidden)] #[export_name = #link_stride] unsafe extern "C" fn #local_stride() -> usize { - ::std::mem::size_of::<#inner>() + ::std::mem::size_of::<#elem>() } } } From bae50eff4ee306fa22c734e20af4866a974316e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 441/2232] Move C++-only to_mangled function to C++ generator --- diff --git a/gen/write.rs b/gen/write.rs index 5898654..0eabca4 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,7 +1,6 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::to_mangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; @@ -921,6 +920,29 @@ fn to_typename(namespace: &Namespace, ty: &Type) -> String { } } +fn to_mangled(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut instance = String::new(); + // Do not apply namespace to built-in type + let is_user_type = Atom::from(ident).is_none(); + if is_user_type { + for name in namespace { + instance += name; + instance += "$"; + } + } + instance += &ident.to_string(); + instance + } + Type::RustBox(ptr) => format!("rust_box${}", to_mangled(namespace, &ptr.inner)), + Type::RustVec(ptr) => format!("rust_vec${}", to_mangled(namespace, &ptr.inner)), + Type::UniquePtr(ptr) => format!("std$unique_ptr${}", to_mangled(namespace, &ptr.inner)), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + _ => unimplemented!(), + } +} + fn write_generic_instantiations(out: &mut OutFile, types: &Types) { fn allow_unique_ptr(ident: &Ident) -> bool { Atom::from(ident).is_none() diff --git a/syntax/mangled.rs b/syntax/mangled.rs deleted file mode 100644 index f52aa43..0000000 --- a/syntax/mangled.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::syntax::namespace::Namespace; -use crate::syntax::{Atom, Type}; - -pub fn to_mangled(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(ident) => { - let mut instance = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - instance += name; - instance += "$"; - } - } - instance += &ident.to_string(); - instance - } - Type::RustBox(ptr) => format!("rust_box${}", to_mangled(namespace, &ptr.inner)), - Type::RustVec(ptr) => format!("rust_vec${}", to_mangled(namespace, &ptr.inner)), - Type::UniquePtr(ptr) => format!("std$unique_ptr${}", to_mangled(namespace, &ptr.inner)), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), - _ => unimplemented!(), - } -} diff --git a/syntax/mod.rs b/syntax/mod.rs index d5b90f0..a4b0ac4 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -8,7 +8,6 @@ pub mod error; pub mod ident; mod impls; pub mod mangle; -pub mod mangled; pub mod namespace; mod parse; pub mod set; From acdf20ae87f2185869991db815b8e33a71903e49 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 442/2232] Simplify to_mangled to match usage --- diff --git a/gen/write.rs b/gen/write.rs index 0eabca4..8fc698c 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -920,26 +920,13 @@ fn to_typename(namespace: &Namespace, ty: &Type) -> String { } } +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. fn to_mangled(namespace: &Namespace, ty: &Type) -> String { match ty { - Type::Ident(ident) => { - let mut instance = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - instance += name; - instance += "$"; - } - } - instance += &ident.to_string(); - instance - } - Type::RustBox(ptr) => format!("rust_box${}", to_mangled(namespace, &ptr.inner)), - Type::RustVec(ptr) => format!("rust_vec${}", to_mangled(namespace, &ptr.inner)), - Type::UniquePtr(ptr) => format!("std$unique_ptr${}", to_mangled(namespace, &ptr.inner)), + Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), - _ => unimplemented!(), + _ => unreachable!(), } } From 9b304204b7c11f814d90ad2318e49ce7886537e7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 443/2232] Use more appropriate name for vector arguments in tests --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 0d904fc..a16775b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -41,9 +41,9 @@ pub mod ffi { fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); - fn c_take_unique_ptr_vector_u8(s: UniquePtr>); - fn c_take_unique_ptr_vector_f64(s: UniquePtr>); - fn c_take_unique_ptr_vector_shared(s: UniquePtr>); + fn c_take_unique_ptr_vector_u8(v: UniquePtr>); + fn c_take_unique_ptr_vector_f64(v: UniquePtr>); + fn c_take_unique_ptr_vector_shared(v: UniquePtr>); fn c_take_vec_u8(v: &Vec); fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); From 1bcc9fe2e572da51f1e2ac2e5cda14ac971e6bea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 444/2232] Test returning opaque types in CxxVector from C to Rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index a16775b..d6a94e4 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -30,6 +30,7 @@ pub mod ffi { fn c_return_unique_ptr_vector_u8() -> UniquePtr>; fn c_return_unique_ptr_vector_f64() -> UniquePtr>; fn c_return_unique_ptr_vector_shared() -> UniquePtr>; + fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 502be05..372641c 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -83,6 +83,10 @@ std::unique_ptr> c_return_unique_ptr_vector_shared() { return vec; } +std::unique_ptr> c_return_unique_ptr_vector_opaque() { + return std::unique_ptr>(new std::vector()); +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index e1e163d..ed07ce6 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -32,6 +32,7 @@ std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); +std::unique_ptr> c_return_unique_ptr_vector_opaque(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); From 2244d1f553e2d1c6faa5500d9c11af0cf262347e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:17 +0000 Subject: [PATCH 445/2232] Test passing CxxVector by reference from Rust to C --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d6a94e4..750b30e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -45,6 +45,7 @@ pub mod ffi { fn c_take_unique_ptr_vector_u8(v: UniquePtr>); fn c_take_unique_ptr_vector_f64(v: UniquePtr>); fn c_take_unique_ptr_vector_shared(v: UniquePtr>); + fn c_take_ref_vector(v: &CxxVector); fn c_take_vec_u8(v: &Vec); fn c_take_vec_shared(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 372641c..04f5168 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -166,6 +166,12 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { } } +void c_take_ref_vector(const std::vector &v) { + if (v.size() == 4) { + cxx_test_suite_set_correct(); + } +} + void c_take_vec_u8(const rust::Vec &v) { uint8_t sum = std::accumulate(v.begin(), v.end(), 0); if (sum == 200) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index ed07ce6..2c7428d 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -47,6 +47,7 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); +void c_take_ref_vector(const std::vector &v); void c_take_vec_u8(const rust::Vec &v); void c_take_vec_shared(const rust::Vec &v); void c_take_callback(rust::Fn callback); diff --git a/tests/test.rs b/tests/test.rs index 1d903f0..771fe9b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -91,6 +91,7 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_vector_shared( ffi::c_return_unique_ptr_vector_shared() )); + check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); check!(ffi::c_take_vec_shared(&vec![ ffi::Shared { z: 1010 }, From de5340ec856ffa78f80dcf85831135de5350084b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 446/2232] Test returning CxxVector by reference from C to Rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 750b30e..4a6ec81 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -31,6 +31,7 @@ pub mod ffi { fn c_return_unique_ptr_vector_f64() -> UniquePtr>; fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; + fn c_return_ref_vector(c: &C) -> &CxxVector; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 04f5168..8503ac7 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -28,6 +28,8 @@ size_t C::set2(size_t n) { return this->n; } +const std::vector &C::get_v() const { return this->v; } + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -87,6 +89,10 @@ std::unique_ptr> c_return_unique_ptr_vector_opaque() { return std::unique_ptr>(new std::vector()); } +const std::vector &c_return_ref_vector(const C &c) { + return c.get_v(); +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 2c7428d..0e9081a 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -15,9 +15,11 @@ public: size_t set(size_t n); size_t get2() const; size_t set2(size_t n); + const std::vector &get_v() const; private: size_t n; + std::vector v; }; size_t c_return_primitive(); @@ -33,6 +35,7 @@ std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); +const std::vector &c_return_ref_vector(const C &c); void c_take_primitive(size_t n); void c_take_shared(Shared shared); From 99c93d864c55faecfa4b1a3bbbc27dceb8a0aaca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 447/2232] Add vector types to reserved names list --- diff --git a/syntax/check.rs b/syntax/check.rs index fd5ed67..4f52cb6 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -290,7 +290,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } fn check_reserved_name(cx: &mut Check, ident: &Ident) { - if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { + if ident == "Box" + || ident == "UniquePtr" + || ident == "Vec" + || ident == "CxxVector" + || Atom::from(ident).is_some() + { cx.error(ident, "reserved name"); } } From 51cc8ee0618e262422ee547f5f5150391e9cc3cf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 448/2232] Fix module path of Vec in generated Rust code --- diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 2962a2b..13cbfcf 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -35,9 +35,12 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { - if let "UniquePtr" | "RustVec" | "CxxVector" = self.name.to_string().as_str() { - let span = self.name.span(); + let span = self.name.span(); + let name = self.name.to_string(); + if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); + } else if name == "Vec" { + tokens.extend(quote_spanned!(span=> ::std::vec::)); } self.name.to_tokens(tokens); self.langle.to_tokens(tokens); From e70303c68e2d95e6c6411b6f80ee57212d99081a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 449/2232] Disallow passing CxxVector by move --- diff --git a/syntax/check.rs b/syntax/check.rs index 4f52cb6..b61570c 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -303,7 +303,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, - Type::Slice(_) | Type::Void(_) => return true, + Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) From fa85fce8534636daaa1f86c182711436945b2626 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 450/2232] Clarify mentions of C++ vector in error messages --- diff --git a/syntax/check.rs b/syntax/check.rs index b61570c..0c75de8 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -361,7 +361,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), - Type::CxxVector(_) => "vector".to_owned(), + Type::CxxVector(_) => "C++ vector".to_owned(), Type::Slice(_) => "slice".to_owned(), Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), From b41e74c152d6debf883541a94fb9f29e71e43fec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 451/2232] Test returning Vec by value from C to Rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4a6ec81..0965e52 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -32,6 +32,7 @@ pub mod ffi { fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; + fn c_return_rust_vec() -> Vec; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8503ac7..7867bf0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -93,6 +93,10 @@ const std::vector &c_return_ref_vector(const C &c) { return c.get_v(); } +rust::Vec c_return_rust_vec() { + throw std::runtime_error("unimplemented"); +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 0e9081a..9a067d2 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -36,6 +36,7 @@ std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); +rust::Vec c_return_rust_vec(); void c_take_primitive(size_t n); void c_take_shared(Shared shared); From e6d5021522cb675897afdf627f7cc25b3fea471e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 452/2232] Fix expansion of Rust Vec in extern signatures --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e3ab530..cda77db 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -746,7 +746,10 @@ fn expand_extern_type(ty: &Type) -> TokenStream { let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) } - Type::RustVec(ty) => quote!(::cxx::private::RustVec<#ty>), + Type::RustVec(ty) => { + let elem = expand_extern_type(&ty.inner); + quote!(::cxx::private::RustVec<#elem>) + } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), Type::RustVec(ty) => { From 03dca701619a0d4b2018eba6afaf0aa245cec077 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:18 +0000 Subject: [PATCH 453/2232] Fix Vec returns by value --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index cda77db..35603c9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -296,6 +296,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), + Type::RustVec(_) => Some(quote!(#call.into_vec())), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), diff --git a/syntax/types.rs b/syntax/types.rs index 2ef1e1e..f3aaa16 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -96,6 +96,7 @@ impl<'a> Types<'a> { Atom::from(ident) == Some(RustString) } } + Type::RustVec(_) => true, _ => false, } } From 8b9d176ea73a5ceb1db42c8b2e5660ccafc7f685 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 454/2232] Fix and test fallible return of Vec from C to Rust --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 35603c9..d27c15c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -281,6 +281,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), + Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 0965e52..6e636d8 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -61,6 +61,7 @@ pub mod ffi { fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; + fn c_try_return_rust_vec() -> Result>; fn get(self: &C) -> usize; fn set(self: &mut C, n: usize) -> usize; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 7867bf0..8ea3d06 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -223,6 +223,10 @@ std::unique_ptr c_try_return_unique_ptr_string() { return c_return_unique_ptr_string(); } +rust::Vec c_try_return_rust_vec() { + throw std::runtime_error("unimplemented"); +} + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 9a067d2..22fed58 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -65,5 +65,6 @@ rust::Str c_try_return_str(rust::Str); rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); +rust::Vec c_try_return_rust_vec(); } // namespace tests From 7798969827735b267951e594a1d1b4ac4667e31f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 455/2232] Fix and test returning Vec by reference from C to Rust --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d27c15c..e7eece0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -287,6 +287,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::Ident(ident) if ident == RustString => { Some(quote!(#call.map(|r| r.as_string()))) } + Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))), _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), @@ -301,6 +302,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), + Type::RustVec(_) => Some(quote!(#call.as_vec())), _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 6e636d8..d66bcc0 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -33,6 +33,7 @@ pub mod ffi { fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; fn c_return_rust_vec() -> Vec; + fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -62,6 +63,7 @@ pub mod ffi { fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; fn c_try_return_rust_vec() -> Result>; + fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec>; fn get(self: &C) -> usize; fn set(self: &mut C, n: usize) -> usize; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8ea3d06..805e9b9 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -97,6 +97,11 @@ rust::Vec c_return_rust_vec() { throw std::runtime_error("unimplemented"); } +const rust::Vec &c_return_ref_rust_vec(const C &c) { + (void)c; + throw std::runtime_error("unimplemented"); +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -227,6 +232,11 @@ rust::Vec c_try_return_rust_vec() { throw std::runtime_error("unimplemented"); } +const rust::Vec &c_try_return_ref_rust_vec(const C &c) { + (void)c; + throw std::runtime_error("unimplemented"); +} + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 22fed58..81125f8 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -37,6 +37,7 @@ std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); rust::Vec c_return_rust_vec(); +const rust::Vec &c_return_ref_rust_vec(const C &c); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -66,5 +67,6 @@ rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); rust::Vec c_try_return_rust_vec(); +const rust::Vec &c_try_return_ref_rust_vec(const C &c); } // namespace tests From d2ce8a997b65aa7b733e4e026b6a90963374a46d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 456/2232] Fix and test passing Vec by value from Rust to C --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e7eece0..e0f3cc7 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -207,7 +207,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), - Type::RustVec(_) => quote!(::cxx::private::RustVec::from(#var)), + Type::RustVec(_) => quote!(#var.as_mut_ptr() as *mut ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d66bcc0..fca9808 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -49,8 +49,9 @@ pub mod ffi { fn c_take_unique_ptr_vector_f64(v: UniquePtr>); fn c_take_unique_ptr_vector_shared(v: UniquePtr>); fn c_take_ref_vector(v: &CxxVector); - fn c_take_vec_u8(v: &Vec); - fn c_take_vec_shared(v: &Vec); + fn c_take_rust_vec(v: Vec); + fn c_take_rust_vec_shared(v: Vec); + fn c_take_ref_rust_vec(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 805e9b9..c0b5888 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -187,14 +187,9 @@ void c_take_ref_vector(const std::vector &v) { } } -void c_take_vec_u8(const rust::Vec &v) { - uint8_t sum = std::accumulate(v.begin(), v.end(), 0); - if (sum == 200) { - cxx_test_suite_set_correct(); - } -} +void c_take_rust_vec(rust::Vec v) { c_take_ref_rust_vec(v); } -void c_take_vec_shared(const rust::Vec &v) { +void c_take_rust_vec_shared(rust::Vec v) { uint32_t sum = 0; for (auto i : v) { sum += i.z; @@ -204,6 +199,13 @@ void c_take_vec_shared(const rust::Vec &v) { } } +void c_take_ref_rust_vec(const rust::Vec &v) { + uint8_t sum = std::accumulate(v.begin(), v.end(), 0); + if (sum == 200) { + cxx_test_suite_set_correct(); + } +} + void c_take_callback(rust::Fn callback) { callback("2020"); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 81125f8..1d617ab 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -53,8 +53,9 @@ void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); -void c_take_vec_u8(const rust::Vec &v); -void c_take_vec_shared(const rust::Vec &v); +void c_take_rust_vec(rust::Vec v); +void c_take_rust_vec_shared(rust::Vec v); +void c_take_ref_rust_vec(const rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); diff --git a/tests/test.rs b/tests/test.rs index 771fe9b..07c23bd 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -92,11 +92,14 @@ fn test_c_take() { ffi::c_return_unique_ptr_vector_shared() )); check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); - check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); - check!(ffi::c_take_vec_shared(&vec![ + check!(ffi::c_take_rust_vec([86_u8, 75_u8, 30_u8, 9_u8].to_vec())); + check!(ffi::c_take_rust_vec_shared(vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } ])); + check!(ffi::c_take_ref_rust_vec( + &[86_u8, 75_u8, 30_u8, 9_u8].to_vec() + )); } #[test] From 313b10ed1e9ed78cfaf36f767f05653f6c11fc54 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 457/2232] Add const bitcopy constructor for Vec --- diff --git a/gen/write.rs b/gen/write.rs index 8fc698c..ccd6c2f 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -186,7 +186,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_trycatch = true; } for arg in &efn.args { - if arg.ty == RustString { + let bitcopy = match arg.ty { + Type::RustVec(_) => true, + _ => arg.ty == RustString, + }; + if bitcopy { needs_unsafe_bitcopy = true; break; } @@ -389,6 +393,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } if arg.ty == RustString { write!(out, "const "); + } else if let Type::RustVec(_) = arg.ty { + write!(out, "const "); } write_extern_arg(out, arg, types); } @@ -467,6 +473,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident, ); + } else if let Type::RustVec(_) = arg.ty { + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); diff --git a/include/cxx.h b/include/cxx.h index e16d9e0..3dc1c4d 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,7 +15,13 @@ namespace rust { inline namespace cxxbridge02 { -struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_BITCOPY +#define CXXBRIDGE02_RUST_BITCOPY +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE02_RUST_BITCOPY #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING @@ -249,6 +255,9 @@ public: return it; } + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} + private: static size_t stride() noexcept; void drop() noexcept; @@ -316,14 +325,6 @@ using fn = Fn; template using try_fn = TryFn; -#ifndef CXXBRIDGE02_RUST_BITCOPY -#define CXXBRIDGE02_RUST_BITCOPY -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE02_RUST_BITCOPY - template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e0f3cc7..af404d0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -146,6 +146,8 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ty = expand_extern_type(&arg.ty); if arg.ty == RustString { quote!(#ident: *const #ty) + } else if let Type::RustVec(_) = arg.ty { + quote!(#ident: *const #ty) } else if let Type::Fn(_) = arg.ty { quote!(#ident: ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { @@ -207,7 +209,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), - Type::RustVec(_) => quote!(#var.as_mut_ptr() as *mut ::cxx::private::RustVec<_>), + Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) From f97c2d51ed6a6f3dbfb26eedba51912153e41111 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 458/2232] Add construction and assignment for rust::Vec --- diff --git a/gen/write.rs b/gen/write.rs index ccd6c2f..cdbfc75 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1028,6 +1028,11 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); writeln!( out, + "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); @@ -1075,6 +1080,11 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { let instance = to_mangled(&out.namespace, &element); writeln!(out, "template <>"); + writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); + writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!( out, diff --git a/include/cxx.h b/include/cxx.h index 3dc1c4d..c02d3d9 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -213,8 +213,22 @@ class Vec final { public: using value_type = T; + Vec() noexcept; + Vec(Vec &&other) noexcept { + this->repr = other.repr; + new (&other) Vec(); + } ~Vec() noexcept { this->drop(); } + Vec &operator=(Vec &&other) noexcept { + if (this != &other) { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + } + return *this; + } + size_t size() const noexcept; bool empty() const noexcept { return size() == 0; } const T *data() const noexcept; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index af404d0..2c8cc87 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -543,12 +543,14 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { let link_prefix = format!("cxxbridge02$rust_vec${}{}$", namespace, elem); + let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_data = format!("{}data", link_prefix); let link_stride = format!("{}stride", link_prefix); let local_prefix = format_ident!("{}__vec_", elem); + let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); let local_data = format_ident!("{}data", local_prefix); @@ -557,6 +559,11 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { let span = elem.span(); quote_spanned! {span=> #[doc(hidden)] + #[export_name = #link_new] + unsafe extern "C" fn #local_new(this: *mut ::cxx::private::RustVec<#elem>) { + ::std::ptr::write(this, ::cxx::private::RustVec::new()); + } + #[doc(hidden)] #[export_name = #link_drop] unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) { ::std::ptr::drop_in_place(this); diff --git a/src/cxx.cc b/src/cxx.cc index 63b2166..7465f56 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -237,6 +237,8 @@ void cxxbridge02$unique_ptr$std$string$drop( } #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge02$rust_vec$##RUST_TYPE##$new( \ + rust::Vec *ptr) noexcept; \ void cxxbridge02$rust_vec$##RUST_TYPE##$drop( \ rust::Vec *ptr) noexcept; \ size_t cxxbridge02$rust_vec$##RUST_TYPE##$len( \ @@ -247,6 +249,10 @@ void cxxbridge02$unique_ptr$std$string$drop( #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ + rust::Vec::Vec() noexcept { \ + cxxbridge02$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ void rust::Vec::drop() noexcept { \ return cxxbridge02$rust_vec$##RUST_TYPE##$drop(this); \ } \ diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 564851e..d5de489 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -7,6 +7,10 @@ pub struct RustVec { } impl RustVec { + pub fn new() -> Self { + RustVec { repr: Vec::new() } + } + pub fn from(v: Vec) -> Self { RustVec { repr: v } } @@ -43,6 +47,12 @@ macro_rules! rust_vec_shims_for_primitive { const _: () = { attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$new")] + unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { + ptr::write(this, RustVec::new()); + } + } + attr! { #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); From 83c69e92f73940237caa70572903b836943ef312 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:02:19 +0000 Subject: [PATCH 459/2232] Test Rust Vec in Rust signatures --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2c8cc87..be58cbd 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -427,9 +427,11 @@ fn expand_rust_function_shim_impl( quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), + Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i == RustString => quote!(#ident.as_string()), + Type::RustVec(_) => quote!(#ident.as_vec()), _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), @@ -460,11 +462,13 @@ fn expand_rust_function_shim_impl( Some(quote!(::cxx::private::RustString::from(#call))) } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), + Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { Some(quote!(::cxx::private::RustString::from_ref(#call))) } + Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))), _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fca9808..d2c860e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -84,6 +84,8 @@ pub mod ffi { fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; + fn r_return_rust_vec() -> Vec; + fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -95,6 +97,8 @@ pub mod ffi { fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + fn r_take_rust_vec(v: Vec); + fn r_take_ref_rust_vec(v: &Vec); fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; @@ -171,6 +175,15 @@ fn r_return_unique_ptr_string() -> UniquePtr { unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) } } +fn r_return_rust_vec() -> Vec { + Vec::new() +} + +fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { + let _ = shared; + unimplemented!() +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } @@ -212,6 +225,14 @@ fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } +fn r_take_rust_vec(v: Vec) { + let _ = v; +} + +fn r_take_ref_rust_vec(v: &Vec) { + let _ = v; +} + fn r_try_return_void() -> Result<(), Error> { Ok(()) } From 1768d8fd07b39a1ee970efe5bde49a1d72fb5669 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:15:14 +0000 Subject: [PATCH 460/2232] Specialize only within the same namespace Without this, on some compilers: src/cxx.cc:252:30: error: specialization of ‘template rust::cxxbridge02::Vec::Vec()’ in different namespace [-fpermissive] rust::Vec::Vec() noexcept { ^ src/cxx.cc:274:3: note: in expansion of macro ‘RUST_VEC_OPS’ MACRO(u8, uint8_t) ^ src/cxx.cc:295:1: note: in expansion of macro ‘FOR_EACH_SIZED_PRIMITIVE’ FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) ^ In file included from src/cxx.cc:1:0: include/cxx.h:216:3: error: from definition of ‘template rust::cxxbridge02::Vec::Vec()’ [-fpermissive] Vec() noexcept; ^ --- diff --git a/src/cxx.cc b/src/cxx.cc index 7465f56..0ead3f0 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -249,23 +249,23 @@ void cxxbridge02$unique_ptr$std$string$drop( #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ - rust::Vec::Vec() noexcept { \ + Vec::Vec() noexcept { \ cxxbridge02$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ - void rust::Vec::drop() noexcept { \ + void Vec::drop() noexcept { \ return cxxbridge02$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ - size_t rust::Vec::size() const noexcept { \ + size_t Vec::size() const noexcept { \ return cxxbridge02$rust_vec$##RUST_TYPE##$len(this); \ } \ template <> \ - const CXX_TYPE *rust::Vec::data() const noexcept { \ + const CXX_TYPE *Vec::data() const noexcept { \ return cxxbridge02$rust_vec$##RUST_TYPE##$data(this); \ } \ template <> \ - size_t rust::Vec::stride() noexcept { \ + size_t Vec::stride() noexcept { \ return cxxbridge02$rust_vec$##RUST_TYPE##$stride(); \ } @@ -292,4 +292,8 @@ FOR_EACH_PRIMITIVE(STD_VECTOR_OPS) FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) } // extern "C" +namespace rust { +inline namespace cxxbridge02 { FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) +} // namespace cxxbridge02 +} // namespace rust From 47ee894a66a4203ce2497eff1c4f4943c2d21a4c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 01:26:54 +0000 Subject: [PATCH 461/2232] Merge pull request #148 from dtolnay/vec Vector fixes --- diff --git a/README.md b/README.md index a8f4093..f967762 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,8 @@ returns of functions. + +
name in Rustname in C++
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
tbdstd::shared_ptr<T>
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot hold opaque Rust type
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
@@ -317,11 +319,9 @@ matter of designing a nice API for each in its non-native language. - - diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index 4940834..cd447ea 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -15,35 +15,7 @@ std::unique_ptr make_demo(rust::Str appname) { const std::string &get_name(const ThingC &thing) { return thing.appname; } -std::unique_ptr> do_thing(SharedThing state) { - print_r(*state.y); - auto vec = std::unique_ptr>(new std::vector()); - for (uint8_t i = 0; i < 10; i++) { - vec->push_back(i * i); - } - return vec; -} - -JsonBlob get_jb(const ::rust::Vec &vec) { - JsonBlob retval; - - std::cout << "incoming vec length is " << vec.size() << "\n"; - auto vec_copy = static_cast>(vec); - std::cout << "vec_copy length is " << vec_copy.size() << "\n"; - std::cout << "vec_copy[0] is " << (int)vec_copy[0] << "\n"; - - auto blob = std::unique_ptr>(new std::vector()); - for (uint8_t i = 0; i < 10; i++) { - blob->push_back(i * 2); - } - - auto json = std::unique_ptr(new std::string("{\"demo\": 23}")); - - retval.json = std::move(json); - retval.blob = std::move(blob); - - return retval; -} +void do_thing(SharedThing state) { print_r(*state.y); } } // namespace example } // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h index 2c9f1c0..fafc474 100644 --- a/demo-cxx/demo.h +++ b/demo-cxx/demo.h @@ -15,12 +15,10 @@ public: }; struct SharedThing; -struct JsonBlob; std::unique_ptr make_demo(rust::Str appname); const std::string &get_name(const ThingC &thing); -std::unique_ptr> do_thing(SharedThing state); -JsonBlob get_jb(const ::rust::Vec &vec); +void do_thing(SharedThing state); } // namespace example } // namespace org diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs index 8bf9926..66dfc79 100644 --- a/demo-rs/src/main.rs +++ b/demo-rs/src/main.rs @@ -6,19 +6,13 @@ mod ffi { x: UniquePtr, } - struct JsonBlob { - json: UniquePtr, - blob: UniquePtr>, - } - extern "C" { include!("demo-cxx/demo.h"); type ThingC; fn make_demo(appname: &str) -> UniquePtr; fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing) -> UniquePtr>; - fn get_jb(v: &Vec) -> JsonBlob; + fn do_thing(state: SharedThing); } extern "Rust" { @@ -37,24 +31,9 @@ fn main() { let x = ffi::make_demo("demo of cxx::bridge"); println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - let vec = ffi::do_thing(ffi::SharedThing { + ffi::do_thing(ffi::SharedThing { z: 222, y: Box::new(ThingR(333)), x, }); - - println!("vec length = {}", vec.as_ref().unwrap().size()); - for (i, v) in vec.as_ref().unwrap().into_iter().enumerate() { - println!("vec[{}] = {}", i, v); - } - - let mut rv: Vec = Vec::new(); - for _ in 0..1000 { - rv.push(33); - } - let jb = ffi::get_jb(&rv); - println!("json: {}", jb.json.as_ref().unwrap()); - for (i, v) in jb.blob.as_ref().unwrap().into_iter().enumerate() { - println!("jb.blob[{}] = {}", i, v); - } } diff --git a/gen/include.rs b/gen/include.rs index 077d03e..129a8e6 100644 --- a/gen/include.rs +++ b/gen/include.rs @@ -36,9 +36,9 @@ pub struct Includes { pub exception: bool, pub memory: bool, pub string: bool, - pub vector: bool, pub type_traits: bool, pub utility: bool, + pub vector: bool, pub base_tsd: bool, } @@ -90,6 +90,9 @@ impl Display for Includes { if self.utility { writeln!(f, "#include ")?; } + if self.vector { + writeln!(f, "#include ")?; + } if self.base_tsd { writeln!(f, "#if defined(_WIN32)")?; writeln!(f, "#include ")?; diff --git a/gen/write.rs b/gen/write.rs index 8a60f17..cdbfc75 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1,10 +1,8 @@ use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::ToTypename; use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -125,7 +123,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, - Type::Vector(_) => out.include.vector = true, + Type::CxxVector(_) => out.include.vector = true, Type::SliceRefU8(_) => out.include.cstdint = true, _ => {} } @@ -147,6 +145,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_box = true; } Type::RustVec(_) => { + out.include.array = true; out.include.type_traits = true; needs_rust_vec = true; } @@ -187,7 +186,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_trycatch = true; } for arg in &efn.args { - if arg.ty == RustString { + let bitcopy = match arg.ty { + Type::RustVec(_) => true, + _ => arg.ty == RustString, + }; + if bitcopy { needs_unsafe_bitcopy = true; break; } @@ -390,6 +393,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } if arg.ty == RustString { write!(out, "const "); + } else if let Type::RustVec(_) = arg.ty { + write!(out, "const "); } write_extern_arg(out, arg, types); } @@ -468,6 +473,9 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident, ); + } else if let Type::RustVec(_) = arg.ty { + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); @@ -479,7 +487,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Vector(_)) => write!( + Some(Type::CxxVector(_)) => write!( out, " /* Use RVO to convert to r-value and move construct */" ), @@ -793,7 +801,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: & fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } @@ -810,7 +818,21 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(a) => write!(out, "{}", a.to_cxx()), + Some(Bool) => write!(out, "bool"), + Some(U8) => write!(out, "uint8_t"), + Some(U16) => write!(out, "uint16_t"), + Some(U32) => write!(out, "uint32_t"), + Some(U64) => write!(out, "uint64_t"), + Some(Usize) => write!(out, "size_t"), + Some(I8) => write!(out, "int8_t"), + Some(I16) => write!(out, "int16_t"), + Some(I32) => write!(out, "int32_t"), + Some(I64) => write!(out, "int64_t"), + Some(Isize) => write!(out, "::rust::isize"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), + Some(CxxString) => write!(out, "::std::string"), + Some(RustString) => write!(out, "::rust::String"), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { @@ -828,7 +850,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { write_type(out, &ptr.inner); write!(out, ">"); } - Type::Vector(ty) => { + Type::CxxVector(ty) => { write!(out, "::std::vector<"); write_type(out, &ty.inner); write!(out, ">"); @@ -880,7 +902,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) - | Type::Vector(_) + | Type::CxxVector(_) | Type::RustVec(_) | Type::SliceRefU8(_) | Type::Fn(_) => write!(out, " "), @@ -889,13 +911,36 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { } } -fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_typename(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut path = String::new(); + for name in namespace { + path += name; + path += "::"; + } + path += &ident.to_string(); + path + } + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + _ => unreachable!(), + } +} + +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_mangled(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + _ => unreachable!(), } +} - fn allow_vector(ident: &Ident) -> bool { - // Note: built-in types such as u8 are already defined in cxx.cc +fn write_generic_instantiations(out: &mut OutFile, types: &Types) { + fn allow_unique_ptr(ident: &Ident) -> bool { Atom::from(ident).is_none() } @@ -907,29 +952,24 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { write_rust_box_extern(out, inner); } } else if let Type::RustVec(ty) = ty { - if let Type::Ident(_) = &ty.inner { - out.next_section(); - write_rust_vec_extern(out, &ty.inner); + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_rust_vec_extern(out, inner); + } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if allow_unique_ptr(inner) { out.next_section(); - write_unique_ptr(out, &ptr.inner, types); - } - } else if let Type::Vector(ptr1) = &ptr.inner { - if let Type::Ident(inner) = &ptr1.inner { - if allow_vector(inner) { - out.next_section(); - write_unique_ptr(out, &ptr.inner, types); - } + write_unique_ptr(out, inner, types); } } - } else if let Type::Vector(ptr) = ty { + } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if allow_vector(inner) { + if Atom::from(inner).is_none() { out.next_section(); - write_vector(out, inner); + write_cxx_vector(out, ty, inner, types); } } } @@ -944,8 +984,10 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { write_rust_box_impl(out, inner); } } else if let Type::RustVec(ty) = ty { - if let Type::Ident(_) = &ty.inner { - write_rust_vec_impl(out, &ty.inner); + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + write_rust_vec_impl(out, inner); + } } } } @@ -977,28 +1019,38 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) { - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); +fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); writeln!( out, - "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const std::vector<{}> &vector) noexcept;", - instance, inner, inner + "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + instance, inner, ); writeln!( out, "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); + writeln!( + out, + "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + inner, instance, + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$stride() noexcept;", + instance, + ); writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); } @@ -1022,17 +1074,22 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); +fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "template <>"); + writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); + writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); + writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!( out, " return cxxbridge02$rust_vec${}$drop(this);", - instance + instance, ); writeln!(out, "}}"); @@ -1042,27 +1099,43 @@ fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) { writeln!(out, "}}"); writeln!(out, "template <>"); + writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); writeln!( out, - "Vec<{}>::operator std::vector<{}>() const noexcept {{", - inner, inner - ); - writeln!( - out, - " std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;", - inner, instance, + " return cxxbridge02$rust_vec${}$data(this);", + instance, ); writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); + writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { - out.include.utility = true; - let namespace = out.namespace.iter().cloned().collect::>(); - let inner = ty.to_typename(&namespace); - let instance = ty.to_mangled(&namespace); +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { + let ty = Type::Ident(ident.clone()); + let instance = to_mangled(&out.namespace, &ty); writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + + write_unique_ptr_common(out, &ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); +} + +// Shared by UniquePtr and UniquePtr>. +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.utility = true; + let inner = to_typename(&out.namespace, ty); + let instance = to_mangled(&out.namespace, ty); + + let can_construct_from_value = match ty { + Type::Ident(ident) => types.structs.contains_key(ident), + _ => false, + }; + writeln!( out, "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", @@ -1080,21 +1153,18 @@ fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); - match ty { - Type::Ident(ident) if types.structs.contains_key(ident) => { - writeln!( + if can_construct_from_value { + writeln!( out, "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); - } - _ => (), + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); } writeln!( out, @@ -1124,52 +1194,38 @@ fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!(out, " ptr->~unique_ptr();"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); } -fn write_vector(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in &out.namespace { - inner += name; - inner += "::"; - } - } - let mut instance = inner.clone(); - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - instance += &ident.to_string(); - let instance = instance.replace("::", "$"); +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE02_vector_{}", instance); - writeln!(out, "#define CXXBRIDGE02_vector_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); writeln!( out, - "size_t cxxbridge02$std$vector${}$length(const std::vector<{}> &s) noexcept {{", + "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); writeln!(out, "}}"); - writeln!( out, - "void cxxbridge02$std$vector${}$push_back(std::vector<{}> &s, const {} &item) noexcept {{", - instance, inner, inner + "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, ); - writeln!(out, " s.push_back(item);"); + writeln!(out, " return s[pos];"); writeln!(out, "}}"); - writeln!( out, - "const {} *cxxbridge02$std$vector${}$get_unchecked(const std::vector<{}> &s, size_t pos) noexcept {{", - inner, instance, inner, + "void cxxbridge02$std$vector${}$push_back(::std::vector<{}> &s, const {} &item) noexcept {{", + instance, inner, inner ); - writeln!(out, " return &s[pos];"); + writeln!(out, " s.push_back(item);"); writeln!(out, "}}"); - writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance); + + write_unique_ptr_common(out, vector_ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); } diff --git a/include/cxx.h b/include/cxx.h index 8f03cbb..c02d3d9 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,7 +15,13 @@ namespace rust { inline namespace cxxbridge02 { -struct unsafe_bitcopy_t; +#ifndef CXXBRIDGE02_RUST_BITCOPY +#define CXXBRIDGE02_RUST_BITCOPY +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE02_RUST_BITCOPY #ifndef CXXBRIDGE02_RUST_STRING #define CXXBRIDGE02_RUST_STRING @@ -84,27 +90,6 @@ private: }; #endif // CXXBRIDGE02_RUST_STR -#ifndef CXXBRIDGE02_RUST_VEC -#define CXXBRIDGE02_RUST_VEC -template -class Vec final { -public: - size_t size() const noexcept; - explicit operator std::vector() const noexcept; - -private: - Vec() noexcept; - Vec(const Vec &other) noexcept; - Vec &operator=(Vec other) noexcept; - void drop() noexcept; - - // Repr - const T *ptr; - size_t len; - size_t capacity; -}; -#endif // CXXBRIDGE02_RUST_VEC - #ifndef CXXBRIDGE02_RUST_SLICE #define CXXBRIDGE02_RUST_SLICE template @@ -221,6 +206,81 @@ private: }; #endif // CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE02_RUST_VEC +#define CXXBRIDGE02_RUST_VEC +template +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(Vec &&other) noexcept { + this->repr = other.repr; + new (&other) Vec(); + } + ~Vec() noexcept { this->drop(); } + + Vec &operator=(Vec &&other) noexcept { + if (this != &other) { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + } + return *this; + } + + size_t size() const noexcept; + bool empty() const noexcept { return size() == 0; } + const T *data() const noexcept; + + class const_iterator { + public: + using value_type = typename std::add_const::type; + using reference = typename std::add_lvalue_reference< + typename std::add_const::type>::type; + + const T &operator*() const { return *static_cast(this->pos); } + const_iterator &operator++() { + this->pos = static_cast(this->pos) + this->stride; + return *this; + } + bool operator==(const const_iterator &other) const { + return this->pos == other.pos; + } + bool operator!=(const const_iterator &other) const { + return this->pos != other.pos; + } + + private: + friend class Vec; + const void *pos; + size_t stride; + }; + + const_iterator begin() const noexcept { + const_iterator it; + it.pos = this->data(); + it.stride = this->stride(); + return it; + } + const_iterator end() const noexcept { + const_iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; + } + + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} + +private: + static size_t stride() noexcept; + void drop() noexcept; + + // Size and alignment statically verified by rust_vec.rs. + std::array repr; +}; +#endif // CXXBRIDGE02_RUST_VEC + #ifndef CXXBRIDGE02_RUST_FN #define CXXBRIDGE02_RUST_FN template @@ -279,14 +339,6 @@ using fn = Fn; template using try_fn = TryFn; -#ifndef CXXBRIDGE02_RUST_BITCOPY -#define CXXBRIDGE02_RUST_BITCOPY -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE02_RUST_BITCOPY - template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 95a6f1a..be58cbd 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,14 +1,12 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::mangled::ToMangled; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::typename::ToTypename; use crate::syntax::{ self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token}; +use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let ident = &ffi.ident; @@ -23,38 +21,6 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - // "Header" to define newtypes locally so we can implement - // traits on them. - expanded.extend(quote! { - pub struct Vector(pub ::cxx::RealVector); - impl> Vector { - pub fn size(&self) -> usize { - self.0.size() - } - pub fn get(&self, pos: usize) -> Option<&T> { - self.0.get(pos) - } - pub fn get_unchecked(&self, pos: usize) -> &T { - self.0.get_unchecked(pos) - } - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - pub fn push_back(&mut self, item: &T) { - self.0.push_back(item) - } - } - impl<'a, T: cxx::private::VectorTarget> IntoIterator for &'a Vector { - type Item = &'a T; - type IntoIter = <&'a ::cxx::RealVector as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } - } - unsafe impl Send for Vector where T: Send + cxx::private::VectorTarget {} - }); - for api in &apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); @@ -89,32 +55,23 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - hidden.extend(expand_rust_vec(namespace, &ty.inner, ident)); + if Atom::from(ident).is_none() { + hidden.extend(expand_rust_vec(namespace, ident)); + } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); - } - } else if let Type::Vector(_) = &ptr.inner { - // Generate code for unique_ptr> if T is not an atom - // or if T is a primitive. - // Code for primitives is already generated - match Atom::from(ident) { - None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)), - Some(atom) => { - if atom.is_valid_vector_target() { - expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)); - } - } + expanded.extend(expand_unique_ptr(namespace, ident, types)); } } - } else if let Type::Vector(ptr) = ty { + } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() { - // Generate code for Vector if T is not an atom - // Code for atoms is already generated - expanded.extend(expand_vector(namespace, &ptr.inner)); + // Generate impl for CxxVector if T is a struct or opaque + // C++ type. Impl for primitives is already provided by cxx + // crate. + expanded.extend(expand_cxx_vector(namespace, ident)); } } } @@ -189,6 +146,8 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ty = expand_extern_type(&arg.ty); if arg.ty == RustString { quote!(#ident: *const #ty) + } else if let Type::RustVec(_) = arg.ty { + quote!(#ident: *const #ty) } else if let Type::Fn(_) = arg.ty { quote!(#ident: ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { @@ -250,12 +209,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), - Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)), + Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { quote!(::cxx::private::RustString::from_ref(#var)) } - Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)), + Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)), _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -324,11 +283,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), + Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { Some(quote!(#call.map(|r| r.as_string()))) } + Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))), _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), @@ -339,9 +300,11 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), + Type::RustVec(_) => Some(quote!(#call.into_vec())), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), + Type::RustVec(_) => Some(quote!(#call.as_vec())), _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), @@ -464,9 +427,11 @@ fn expand_rust_function_shim_impl( quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), + Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i == RustString => quote!(#ident.as_string()), + Type::RustVec(_) => quote!(#ident.as_vec()), _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), @@ -497,11 +462,13 @@ fn expand_rust_function_shim_impl( Some(quote!(::cxx::private::RustString::from(#call))) } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), + Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => { Some(quote!(::cxx::private::RustString::from_ref(#call))) } + Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))), _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), @@ -578,42 +545,54 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream { - let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; - let link_prefix = format!("cxxbridge02$rust_vec${}", mangled); +fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { + let link_prefix = format!("cxxbridge02$rust_vec${}{}$", namespace, elem); + let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); - let link_vector_from = format!("{}vector_from", link_prefix); let link_len = format!("{}len", link_prefix); + let link_data = format!("{}data", link_prefix); + let link_stride = format!("{}stride", link_prefix); - let local_prefix = format_ident!("{}__vec_", ident); + let local_prefix = format_ident!("{}__vec_", elem); + let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); - let local_vector_from = format_ident!("{}vector_from", local_prefix); let local_len = format_ident!("{}len", local_prefix); + let local_data = format_ident!("{}data", local_prefix); + let local_stride = format_ident!("{}stride", local_prefix); - let span = ty.span(); + let span = elem.span(); quote_spanned! {span=> #[doc(hidden)] - #[export_name = #link_drop] - unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) { - std::ptr::drop_in_place(this); + #[export_name = #link_new] + unsafe extern "C" fn #local_new(this: *mut ::cxx::private::RustVec<#elem>) { + ::std::ptr::write(this, ::cxx::private::RustVec::new()); } - #[export_name = #link_vector_from] - unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) { - this.as_ref().unwrap().into_vector(vector.as_mut().unwrap()); + #[doc(hidden)] + #[export_name = #link_drop] + unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) { + ::std::ptr::drop_in_place(this); } + #[doc(hidden)] #[export_name = #link_len] - unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize { - this.as_ref().unwrap().len() + unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#elem>) -> usize { + (*this).len() + } + #[doc(hidden)] + #[export_name = #link_data] + unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#elem>) -> *const #elem { + (*this).as_ptr() + } + #[doc(hidden)] + #[export_name = #link_stride] + unsafe extern "C" fn #local_stride() -> usize { + ::std::mem::size_of::<#elem>() } } } -fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream { - let name = ty.to_typename(&namespace.segments); - let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; - let prefix = format!("cxxbridge02$unique_ptr${}", mangled); +fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { + let name = ident.to_string(); + let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -621,8 +600,8 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = match ty { - Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! { + let new_method = if types.structs.contains_key(ident) { + Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] @@ -632,13 +611,14 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe { __new(&mut repr, &mut value) } repr } - }), - _ => None, + }) + } else { + None }; quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #inner { - const __NAME: &'static str = #name; + unsafe impl ::cxx::private::UniquePtrTarget for #ident { + const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_null] @@ -652,7 +632,7 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_raw] - fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner); + fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident); } let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); __raw(&mut repr, raw); @@ -661,14 +641,14 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self { extern "C" { #[link_name = #link_get] - fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner; + fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident; } __get(&repr) } unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self { extern "C" { #[link_name = #link_release] - fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner; + fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident; } __release(&mut repr) } @@ -683,85 +663,81 @@ fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenSt } } -fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream { - let inner = ty; - let mangled = ty.to_mangled(&namespace.segments) + "$"; - let prefix = format!("cxxbridge02$std$vector${}", mangled); - let link_length = format!("{}length", prefix); +fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { + let name = elem.to_string(); + let prefix = format!("cxxbridge02$std$vector${}{}$", namespace, elem); + let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); let link_push_back = format!("{}push_back", prefix); + let unique_ptr_prefix = format!("cxxbridge02$unique_ptr$std$vector${}{}$", namespace, elem); + let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); + let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); + let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); + let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); + let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); quote! { - impl ::cxx::private::VectorTarget<#inner> for #inner { - fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner { + unsafe impl ::cxx::private::VectorElement for #elem { + const __NAME: &'static dyn ::std::fmt::Display = &#name; + fn __vector_size(v: &::cxx::CxxVector) -> usize { extern "C" { - #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner; + #[link_name = #link_size] + fn __vector_size(_: &::cxx::CxxVector<#elem>) -> usize; } - unsafe { - __get_unchecked(v, pos) + unsafe { __vector_size(v) } + } + unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> &Self { + extern "C" { + #[link_name = #link_get_unchecked] + fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem; } + &*__get_unchecked(v, pos) } - fn vector_length(v: &::cxx::RealVector<#inner>) -> usize { - unsafe { - extern "C" { - #[link_name = #link_length] - fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize; - } - __vector_length(v) + fn __push_back(v: &::cxx::CxxVector, item: &Self) { + extern "C" { + #[link_name = #link_push_back] + fn __push_back(_: &::cxx::CxxVector<#elem>, _: &#elem); } + unsafe { __push_back(v, item) } } - fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) { - unsafe { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize; - } - __push_back(v, item); + fn __unique_ptr_null() -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_unique_ptr_null] + fn __unique_ptr_null(this: *mut *mut ::std::ffi::c_void); } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __unique_ptr_null(&mut repr) } + repr } - } - } -} - -pub fn expand_vector_builtin(ident: Ident) -> TokenStream { - let ty = Type::Ident(ident); - let inner = &ty; - let namespace = Namespace { segments: vec![] }; - let mangled = ty.to_mangled(&namespace.segments) + "$"; - let prefix = format!("cxxbridge02$std$vector${}", mangled); - let link_length = format!("{}length", prefix); - let link_get_unchecked = format!("{}get_unchecked", prefix); - let link_push_back = format!("{}push_back", prefix); - - quote! { - impl VectorTarget<#inner> for #inner { - fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner { + unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector) -> *mut ::std::ffi::c_void { extern "C" { - #[link_name = #link_get_unchecked] - fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner; + #[link_name = #link_unique_ptr_raw] + fn __unique_ptr_raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem>); } - unsafe { - __get_unchecked(v, pos) + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + __unique_ptr_raw(&mut repr, raw); + repr + } + unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector { + extern "C" { + #[link_name = #link_unique_ptr_get] + fn __unique_ptr_get(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem>; } + __unique_ptr_get(&repr) } - fn vector_length(v: &RealVector<#inner>) -> usize { - unsafe { - extern "C" { - #[link_name = #link_length] - fn __vector_length(_: &RealVector<#inner>) -> usize; - } - __vector_length(v) + unsafe fn __unique_ptr_release(mut repr: *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector { + extern "C" { + #[link_name = #link_unique_ptr_release] + fn __unique_ptr_release(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem>; } + __unique_ptr_release(&mut repr) } - fn push_back(v: &RealVector<#inner>, item: &#inner) { - unsafe { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize; - } - __push_back(v, item); + unsafe fn __unique_ptr_drop(mut repr: *mut ::std::ffi::c_void) { + extern "C" { + #[link_name = #link_unique_ptr_drop] + fn __unique_ptr_drop(this: *mut *mut ::std::ffi::c_void); } + __unique_ptr_drop(&mut repr); } } } @@ -787,12 +763,15 @@ fn expand_extern_type(ty: &Type) -> TokenStream { let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) } - Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>), + Type::RustVec(ty) => { + let elem = expand_extern_type(&ty.inner); + quote!(::cxx::private::RustVec<#elem>) + } Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), Type::RustVec(ty) => { let inner = expand_extern_type(&ty.inner); - quote!(&::cxx::RustVec<#inner>) + quote!(&::cxx::private::RustVec<#inner>) } _ => quote!(#ty), }, diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 87fe405..b56f58e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,7 +14,7 @@ mod syntax; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, Ident, ItemMod}; +use syn::{parse_macro_input, ItemMod}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,9 +44,3 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } - -#[proc_macro] -pub fn vector_builtin(input: TokenStream) -> TokenStream { - let ident = parse_macro_input!(input as Ident); - expand::expand_vector_builtin(ident).into() -} diff --git a/src/concat.rs b/src/concat.rs new file mode 100644 index 0000000..e67e50d --- /dev/null +++ b/src/concat.rs @@ -0,0 +1,6 @@ +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} diff --git a/src/cxx.cc b/src/cxx.cc index a0e92ac..0ead3f0 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -200,65 +200,100 @@ void cxxbridge02$unique_ptr$std$string$drop( } // extern "C" #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ - extern "C" { \ - size_t cxxbridge02$std$vector$##RUST_TYPE##$length( \ + size_t cxxbridge02$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ } \ - void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ - std::vector &s, const CXX_TYPE &item) noexcept { \ - s.push_back(item); \ - } \ const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ return &s[pos]; \ } \ - static_assert(sizeof(::std::unique_ptr>) == \ - sizeof(void *), \ - ""); \ - static_assert(alignof(::std::unique_ptr>) == \ - alignof(void *), \ - ""); \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ - ::std::unique_ptr> *ptr) noexcept { \ - new (ptr)::std::unique_ptr>(); \ + void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ + std::vector &s, const CXX_TYPE &item) noexcept { \ + s.push_back(item); \ } \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new( \ - ::std::unique_ptr> *ptr, \ - std::vector *value) noexcept { \ - new (ptr)::std::unique_ptr>( \ - new std::vector(::std::move(*value))); \ + void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ + std::unique_ptr> *ptr) noexcept { \ + new (ptr) std::unique_ptr>(); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ - ::std::unique_ptr> *ptr, \ + std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ - new (ptr)::std::unique_ptr>(raw); \ + new (ptr) std::unique_ptr>(raw); \ } \ - const std::vector * \ - cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ - const ::std::unique_ptr> &ptr) noexcept { \ + const std::vector \ + *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ + const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ - std::vector * \ - cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ - ::std::unique_ptr> &ptr) noexcept { \ + std::vector \ + *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ + std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ - ::std::unique_ptr> *ptr) noexcept { \ + std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ + } + +#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge02$rust_vec$##RUST_TYPE##$new( \ + rust::Vec *ptr) noexcept; \ + void cxxbridge02$rust_vec$##RUST_TYPE##$drop( \ + rust::Vec *ptr) noexcept; \ + size_t cxxbridge02$rust_vec$##RUST_TYPE##$len( \ + const rust::Vec *ptr) noexcept; \ + const CXX_TYPE *cxxbridge02$rust_vec$##RUST_TYPE##$data( \ + const rust::Vec *ptr) noexcept; \ + size_t cxxbridge02$rust_vec$##RUST_TYPE##$stride() noexcept; + +#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ + template <> \ + Vec::Vec() noexcept { \ + cxxbridge02$rust_vec$##RUST_TYPE##$new(this); \ + } \ + template <> \ + void Vec::drop() noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$drop(this); \ } \ - } // extern "C" - -STD_VECTOR_OPS(u8, uint8_t); -STD_VECTOR_OPS(u16, uint16_t); -STD_VECTOR_OPS(u32, uint32_t); -STD_VECTOR_OPS(u64, uint64_t); -STD_VECTOR_OPS(usize, size_t); -STD_VECTOR_OPS(i8, int8_t); -STD_VECTOR_OPS(i16, int16_t); -STD_VECTOR_OPS(i32, int32_t); -STD_VECTOR_OPS(i64, int64_t); -STD_VECTOR_OPS(isize, rust::isize); -STD_VECTOR_OPS(f32, float); -STD_VECTOR_OPS(f64, double); + template <> \ + size_t Vec::size() const noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$len(this); \ + } \ + template <> \ + const CXX_TYPE *Vec::data() const noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$data(this); \ + } \ + template <> \ + size_t Vec::stride() noexcept { \ + return cxxbridge02$rust_vec$##RUST_TYPE##$stride(); \ + } + +// Usize and isize are the same type as one of the below. +#define FOR_EACH_SIZED_PRIMITIVE(MACRO) \ + MACRO(u8, uint8_t) \ + MACRO(u16, uint16_t) \ + MACRO(u32, uint32_t) \ + MACRO(u64, uint64_t) \ + MACRO(i8, int8_t) \ + MACRO(i16, int16_t) \ + MACRO(i32, int32_t) \ + MACRO(i64, int64_t) \ + MACRO(f32, float) \ + MACRO(f64, double) + +#define FOR_EACH_PRIMITIVE(MACRO) \ + FOR_EACH_SIZED_PRIMITIVE(MACRO) \ + MACRO(usize, size_t) \ + MACRO(isize, rust::isize) + +extern "C" { +FOR_EACH_PRIMITIVE(STD_VECTOR_OPS) +FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) +} // extern "C" + +namespace rust { +inline namespace cxxbridge02 { +FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) +} // namespace cxxbridge02 +} // namespace rust diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs new file mode 100644 index 0000000..8294044 --- /dev/null +++ b/src/cxx_vector.rs @@ -0,0 +1,234 @@ +use std::ffi::c_void; +use std::fmt::{self, Display}; +use std::marker::PhantomData; +use std::mem; +use std::ptr; + +/// Binding to C++ `std::vector>`. +/// +/// # Invariants +/// +/// As an invariant of this API and the static analysis of the cxx::bridge +/// macro, in Rust code we can never obtain a `CxxVector` by value. Instead in +/// Rust code we will only ever look at a vector behind a reference or smart +/// pointer, as in `&CxxVector` or `UniquePtr>`. +#[repr(C, packed)] +pub struct CxxVector { + _private: [T; 0], +} + +impl CxxVector +where + T: VectorElement, +{ + /// Returns the number of elements in the vector. + /// + /// Matches the behavior of C++ [std::vector\::size][size]. + /// + /// [size]: https://en.cppreference.com/w/cpp/container/vector/size + pub fn len(&self) -> usize { + T::__vector_size(self) + } + + /// Returns true if the vector contains no elements. + /// + /// Matches the behavior of C++ [std::vector\::empty][empty]. + /// + /// [empty]: https://en.cppreference.com/w/cpp/container/vector/empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns a reference to an element at the given position, or `None` if + /// out of bounds. + pub fn get(&self, pos: usize) -> Option<&T> { + if pos < self.len() { + Some(unsafe { T::__get_unchecked(self, pos) }) + } else { + None + } + } + + /// Returns a reference to an element without doing bounds checking. + /// + /// This is generally not recommended, use with caution! Calling this method + /// with an out-of-bounds index is undefined behavior even if the resulting + /// reference is not used. + /// + /// Matches the behavior of C++ + /// [std::vector\::operator\[\]][operator_at]. + /// + /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at + pub unsafe fn get_unchecked(&self, pos: usize) -> &T { + T::__get_unchecked(self, pos) + } + + /// Appends an element to the back of the vector. + pub fn push_back(&mut self, item: &T) { + T::__push_back(self, item); + } +} + +pub struct Iter<'a, T> { + v: &'a CxxVector, + index: usize, +} + +impl<'a, T> IntoIterator for &'a CxxVector +where + T: VectorElement, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + Iter { v: self, index: 0 } + } +} + +impl<'a, T> Iterator for Iter<'a, T> +where + T: VectorElement, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + self.index = self.index + 1; + self.v.get(self.index - 1) + } +} + +pub struct TypeName { + element: PhantomData, +} + +impl TypeName { + pub const fn new() -> Self { + TypeName { + element: PhantomData, + } + } +} + +impl Display for TypeName +where + T: VectorElement, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "CxxVector<{}>", T::__NAME) + } +} + +// Methods are private; not intended to be implemented outside of cxxbridge +// codebase. +#[doc(hidden)] +pub unsafe trait VectorElement: Sized { + const __NAME: &'static dyn Display; + fn __vector_size(v: &CxxVector) -> usize; + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; + fn __push_back(v: &CxxVector, item: &Self); + fn __unique_ptr_null() -> *mut c_void; + unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; + unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; + unsafe fn __unique_ptr_release(repr: *mut c_void) -> *mut CxxVector; + unsafe fn __unique_ptr_drop(repr: *mut c_void); +} + +macro_rules! impl_vector_element_for_primitive { + ($ty:ident) => { + const_assert_eq!(1, mem::align_of::>()); + + unsafe impl VectorElement for $ty { + const __NAME: &'static dyn Display = &stringify!($ty); + fn __vector_size(v: &CxxVector<$ty>) -> usize { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$size")] + fn __vector_size(_: &CxxVector<$ty>) -> usize; + } + } + unsafe { __vector_size(v) } + } + unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$get_unchecked")] + fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; + } + } + &*__get_unchecked(v, pos) + } + fn __push_back(v: &CxxVector<$ty>, item: &$ty) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")] + fn __push_back(_: &CxxVector<$ty>, _: &$ty); + } + } + unsafe { __push_back(v, item) } + } + fn __unique_ptr_null() -> *mut c_void { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$null")] + fn __unique_ptr_null(this: *mut *mut c_void); + } + } + let mut repr = ptr::null_mut::(); + unsafe { __unique_ptr_null(&mut repr) } + repr + } + unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$raw")] + fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); + } + } + let mut repr = ptr::null_mut::(); + __unique_ptr_raw(&mut repr, raw); + repr + } + unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$get")] + fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; + } + } + __unique_ptr_get(&repr) + } + unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$release")] + fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; + } + } + __unique_ptr_release(&mut repr) + } + unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$drop")] + fn __unique_ptr_drop(this: *mut *mut c_void); + } + } + __unique_ptr_drop(&mut repr); + } + } + }; +} + +impl_vector_element_for_primitive!(u8); +impl_vector_element_for_primitive!(u16); +impl_vector_element_for_primitive!(u32); +impl_vector_element_for_primitive!(u64); +impl_vector_element_for_primitive!(usize); +impl_vector_element_for_primitive!(i8); +impl_vector_element_for_primitive!(i16); +impl_vector_element_for_primitive!(i32); +impl_vector_element_for_primitive!(i64); +impl_vector_element_for_primitive!(isize); +impl_vector_element_for_primitive!(f32); +impl_vector_element_for_primitive!(f64); diff --git a/src/lib.rs b/src/lib.rs index e23be69..e4ef8f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -311,6 +311,8 @@ //! //! //! +//! +//! //! //! //!
name in Rustname in C++
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
tbdstd::shared_ptr<T>
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot hold opaque Rust type
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
@@ -325,11 +327,9 @@ //! //! //! -//! //! //! //! -//! //! //! //! @@ -358,8 +358,11 @@ extern crate link_cplusplus; #[macro_use] mod assert; +#[macro_use] +mod concat; mod cxx_string; +mod cxx_vector; mod error; mod exception; mod function; @@ -374,28 +377,26 @@ mod rust_vec; mod syntax; mod unique_ptr; mod unwind; -mod vector; pub use crate::cxx_string::CxxString; +pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; -pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtr; -pub use crate::vector::RealVector; -pub use crate::vector::VectorIntoIterator; pub use cxxbridge_macro::bridge; // Not public API. #[doc(hidden)] pub mod private { + pub use crate::cxx_vector::VectorElement; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; pub use crate::rust_sliceu8::RustSliceU8; pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; + pub use crate::rust_vec::RustVec; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; - pub use crate::vector::VectorTarget; } use crate::error::Result; diff --git a/src/rust_vec.rs b/src/rust_vec.rs index a28570d..d5de489 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,18 +1,22 @@ -use crate::vector::RealVector; -use crate::vector::VectorTarget; +use std::mem; +use std::ptr; #[repr(C)] -pub struct RustVec> { +pub struct RustVec { repr: Vec, } -impl> RustVec { +impl RustVec { + pub fn new() -> Self { + RustVec { repr: Vec::new() } + } + pub fn from(v: Vec) -> Self { RustVec { repr: v } } pub fn from_ref(v: &Vec) -> &Self { - unsafe { std::mem::transmute::<&Vec, &RustVec>(v) } + unsafe { &*(v as *const Vec as *const RustVec) } } pub fn into_vec(self) -> Vec { @@ -31,9 +35,58 @@ impl> RustVec { self.repr.len() } - pub fn into_vector(&self, vec: &mut RealVector) { - for item in &self.repr { - vec.push_back(item); - } + pub fn as_ptr(&self) -> *const T { + self.repr.as_ptr() } } + +macro_rules! rust_vec_shims_for_primitive { + ($ty:ident) => { + const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); + const_assert_eq!(mem::align_of::(), mem::align_of::>()); + + const _: () = { + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$new")] + unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { + ptr::write(this, RustVec::new()); + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$drop")] + unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { + ptr::drop_in_place(this); + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$len")] + unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { + (*this).len() + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$data")] + unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { + (*this).as_ptr() + } + } + attr! { + #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$stride")] + unsafe extern "C" fn __stride() -> usize { + mem::size_of::<$ty>() + } + } + }; + }; +} + +rust_vec_shims_for_primitive!(u8); +rust_vec_shims_for_primitive!(u16); +rust_vec_shims_for_primitive!(u32); +rust_vec_shims_for_primitive!(u64); +rust_vec_shims_for_primitive!(i8); +rust_vec_shims_for_primitive!(i16); +rust_vec_shims_for_primitive!(i32); +rust_vec_shims_for_primitive!(i64); +rust_vec_shims_for_primitive!(f32); +rust_vec_shims_for_primitive!(f64); diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index b50e870..98b4b7d 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,4 +1,5 @@ use crate::cxx_string::CxxString; +use crate::cxx_vector::{self, CxxVector, VectorElement}; use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::marker::PhantomData; @@ -149,7 +150,7 @@ where // codebase. pub unsafe trait UniquePtrTarget { #[doc(hidden)] - const __NAME: &'static str; + const __NAME: &'static dyn Display; #[doc(hidden)] fn __null() -> *mut c_void; #[doc(hidden)] @@ -186,7 +187,7 @@ extern "C" { } unsafe impl UniquePtrTarget for CxxString { - const __NAME: &'static str = "CxxString"; + const __NAME: &'static dyn Display = &"CxxString"; fn __null() -> *mut c_void { let mut repr = ptr::null_mut::(); unsafe { unique_ptr_std_string_null(&mut repr) } @@ -207,3 +208,25 @@ unsafe impl UniquePtrTarget for CxxString { unique_ptr_std_string_drop(&mut repr); } } + +unsafe impl UniquePtrTarget for CxxVector +where + T: VectorElement + 'static, +{ + const __NAME: &'static dyn Display = &cxx_vector::TypeName::::new(); + fn __null() -> *mut c_void { + T::__unique_ptr_null() + } + unsafe fn __raw(raw: *mut Self) -> *mut c_void { + T::__unique_ptr_raw(raw) + } + unsafe fn __get(repr: *mut c_void) -> *const Self { + T::__unique_ptr_get(repr) + } + unsafe fn __release(repr: *mut c_void) -> *mut Self { + T::__unique_ptr_release(repr) + } + unsafe fn __drop(repr: *mut c_void) { + T::__unique_ptr_drop(repr); + } +} diff --git a/src/vector.rs b/src/vector.rs deleted file mode 100644 index 805bdbb..0000000 --- a/src/vector.rs +++ /dev/null @@ -1,91 +0,0 @@ -pub trait VectorTarget { - fn get_unchecked(v: &RealVector, pos: usize) -> &T - where - Self: Sized; - fn vector_length(v: &RealVector) -> usize - where - Self: Sized; - fn push_back(v: &RealVector, item: &T) - where - Self: Sized; -} - -/// Binding to C++ `std::vector`. -/// -/// # Invariants -/// -/// As an invariant of this API and the static analysis of the cxx::bridge -/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector -/// requires a move constructor and may hold internal pointers, which is not -/// compatible with Rust's move behavior. Instead in Rust code we will only ever -/// look at a Vector through a reference or smart pointer, as in `&Vector` -/// or `UniquePtr`. -#[repr(C)] -pub struct RealVector { - _private: [T; 0], -} - -impl> RealVector { - /// Returns the length of the vector in bytes. - pub fn size(&self) -> usize { - T::vector_length(self) - } - - pub fn get_unchecked(&self, pos: usize) -> &T { - T::get_unchecked(self, pos) - } - - /// Returns true if `self` has a length of zero bytes. - pub fn is_empty(&self) -> bool { - self.size() == 0 - } - - pub fn get(&self, pos: usize) -> Option<&T> { - if pos < self.size() { - Some(self.get_unchecked(pos)) - } else { - None - } - } - - pub fn push_back(&mut self, item: &T) { - T::push_back(self, item); - } -} - -unsafe impl Send for RealVector where T: Send + VectorTarget {} - -pub struct VectorIntoIterator<'a, T> { - v: &'a RealVector, - index: usize, -} - -impl<'a, T: VectorTarget> IntoIterator for &'a RealVector { - type Item = &'a T; - type IntoIter = VectorIntoIterator<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - VectorIntoIterator { v: self, index: 0 } - } -} - -impl<'a, T: VectorTarget> Iterator for VectorIntoIterator<'a, T> { - type Item = &'a T; - fn next(&mut self) -> Option { - self.index = self.index + 1; - self.v.get(self.index - 1) - } -} - -cxxbridge_macro::vector_builtin!(u8); -cxxbridge_macro::vector_builtin!(u16); -cxxbridge_macro::vector_builtin!(u32); -cxxbridge_macro::vector_builtin!(u64); -cxxbridge_macro::vector_builtin!(usize); -cxxbridge_macro::vector_builtin!(i8); -cxxbridge_macro::vector_builtin!(i16); -cxxbridge_macro::vector_builtin!(i32); -cxxbridge_macro::vector_builtin!(i64); -cxxbridge_macro::vector_builtin!(isize); -cxxbridge_macro::vector_builtin!(f32); -cxxbridge_macro::vector_builtin!(f64); diff --git a/syntax/atom.rs b/syntax/atom.rs index c68b3fe..eeea831 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -42,43 +42,6 @@ impl Atom { _ => None, } } - - pub fn to_cxx(&self) -> &'static str { - use self::Atom::*; - match self { - Bool => "bool", - U8 => "uint8_t", - U16 => "uint16_t", - U32 => "uint32_t", - U64 => "uint64_t", - Usize => "size_t", - I8 => "int8_t", - I16 => "int16_t", - I32 => "int32_t", - I64 => "int64_t", - Isize => "::rust::isize", - F32 => "float", - F64 => "double", - CxxString => "::std::string", - RustString => "::rust::String", - } - } - - pub fn is_valid_vector_target(&self) -> bool { - use self::Atom::*; - *self == U8 - || *self == U16 - || *self == U32 - || *self == U64 - || *self == Usize - || *self == I8 - || *self == I16 - || *self == I32 - || *self == I64 - || *self == Isize - || *self == F32 - || *self == F64 - } } impl PartialEq for Ident { diff --git a/syntax/check.rs b/syntax/check.rs index 387f9a6..0c75de8 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -29,9 +29,9 @@ fn do_typecheck(cx: &mut Check) { match ty { Type::Ident(ident) => check_type_ident(cx, ident), Type::RustBox(ptr) => check_type_box(cx, ptr), - Type::RustVec(ptr) => check_type_vec(cx, ptr), + Type::RustVec(ty) => check_type_rust_vec(cx, ty), Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr), - Type::Vector(ptr) => check_type_vector(cx, ptr), + Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr), Type::Ref(ty) => check_type_ref(cx, ty), Type::Slice(ty) => check_type_slice(cx, ty), _ => {} @@ -89,19 +89,22 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unsupported target type of Box"); } -fn check_type_vec(cx: &mut Check, ptr: &Ty1) { - // Vec can contain either user-defined type or u8 - if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).map(|a| a.is_valid_vector_target()) == Some(true) { - return; - } else if cx.types.cxx.contains(ident) { - cx.error(ptr, error::VEC_CXX_TYPE.msg); - } else { +fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { + if let Type::Ident(ident) = &ty.inner { + if cx.types.cxx.contains(ident) { + cx.error(ty, "Rust Vec containing C++ type is not supported yet"); return; } + + match Atom::from(ident) { + None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) => return, + Some(Bool) | Some(RustString) => { /* todo */ } + Some(CxxString) => {} + } } - cx.error(ptr, "unsupported target type of Vec"); + cx.error(ty, "unsupported element type of Vec"); } fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { @@ -114,28 +117,30 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { None | Some(CxxString) => return, _ => {} } - } else if let Type::Vector(_) = &ptr.inner { + } else if let Type::CxxVector(_) = &ptr.inner { return; } cx.error(ptr, "unsupported unique_ptr target type"); } -fn check_type_vector(cx: &mut Check, ptr: &Ty1) { +fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { if cx.types.rust.contains(ident) { - cx.error(ptr, "vector of a Rust type is not supported yet"); + cx.error( + ptr, + "C++ vector containing a Rust type is not supported yet", + ); } match Atom::from(ident) { - None => return, - Some(atom) => { - if atom.is_valid_vector_target() { - return; - } - } + 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) => return, + Some(CxxString) => { /* todo */ } + Some(Bool) | Some(RustString) => {} } } + cx.error(ptr, "unsupported vector target type"); } @@ -285,7 +290,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } fn check_reserved_name(cx: &mut Check, ident: &Ident) { - if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() { + if ident == "Box" + || ident == "UniquePtr" + || ident == "Vec" + || ident == "CxxVector" + || Atom::from(ident).is_some() + { cx.error(ident, "reserved name"); } } @@ -293,7 +303,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { Type::Ident(ident) => ident, - Type::Slice(_) | Type::Void(_) => return true, + Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) @@ -351,7 +361,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::UniquePtr(_) => "unique_ptr".to_owned(), Type::Ref(_) => "reference".to_owned(), Type::Str(_) => "&str".to_owned(), - Type::Vector(_) => "vector".to_owned(), + Type::CxxVector(_) => "C++ vector".to_owned(), Type::Slice(_) => "slice".to_owned(), Type::SliceRefU8(_) => "&[u8]".to_owned(), Type::Fn(_) => "function pointer".to_owned(), diff --git a/syntax/error.rs b/syntax/error.rs index 103a54f..f52d651 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -15,7 +15,6 @@ impl Display for Error { pub static ERRORS: &[Error] = &[ BOX_CXX_TYPE, - VEC_CXX_TYPE, CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, @@ -30,12 +29,6 @@ pub static BOX_CXX_TYPE: Error = Error { note: Some("hint: use UniquePtr<>"), }; -pub static VEC_CXX_TYPE: Error = Error { - msg: "Vec of a C++ type is not supported yet", - label: None, - note: Some("hint: use UniquePtr<>"), -}; - pub static CXXBRIDGE_RESERVED: Error = Error { msg: "identifiers starting with cxxbridge are reserved", label: Some("reserved identifier"), diff --git a/syntax/impls.rs b/syntax/impls.rs index 8e94f06..c34e3e5 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -27,7 +27,7 @@ impl Hash for Type { Type::Ref(t) => t.hash(state), Type::Str(t) => t.hash(state), Type::RustVec(t) => t.hash(state), - Type::Vector(t) => t.hash(state), + Type::CxxVector(t) => t.hash(state), Type::Fn(t) => t.hash(state), Type::Slice(t) => t.hash(state), Type::SliceRefU8(t) => t.hash(state), @@ -47,7 +47,7 @@ impl PartialEq for Type { (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::Vector(lhs), Type::Vector(rhs)) => lhs == rhs, + (Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs, (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs, (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs, (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs, diff --git a/syntax/mangled.rs b/syntax/mangled.rs deleted file mode 100644 index 56e8b73..0000000 --- a/syntax/mangled.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::syntax::{Atom, Type}; - -pub trait ToMangled { - fn to_mangled(&self, namespace: &Vec) -> String; -} - -impl ToMangled for Type { - fn to_mangled(&self, namespace: &Vec) -> String { - match self { - Type::Ident(ident) => { - let mut instance = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - instance += name; - instance += "$"; - } - } - instance += &ident.to_string(); - instance - } - Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)), - Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)), - Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)), - Type::Vector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)), - _ => unimplemented!(), - } - } -} diff --git a/syntax/mod.rs b/syntax/mod.rs index 4e0b908..a4b0ac4 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -8,13 +8,11 @@ pub mod error; pub mod ident; mod impls; pub mod mangle; -pub mod mangled; pub mod namespace; mod parse; pub mod set; pub mod symbol; mod tokens; -pub mod typename; pub mod types; use self::parse::kw; @@ -92,7 +90,7 @@ pub enum Type { UniquePtr(Box), Ref(Box), Str(Box), - Vector(Box), + CxxVector(Box), Fn(Box), Void(Span), Slice(Box), diff --git a/syntax/namespace.rs b/syntax/namespace.rs index a4e972b..d26bb9e 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -11,7 +11,7 @@ mod kw { #[derive(Clone)] pub struct Namespace { - pub segments: Vec, + segments: Vec, } impl Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index f5c598d..d14806d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,10 +300,10 @@ fn parse_type_path(ty: &TypePath) -> Result { rangle: generic.gt_token, }))); } - } else if ident == "Vector" && generic.args.len() == 1 { + } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg)?; - return Ok(Type::Vector(Box::new(Ty1 { + return Ok(Type::CxxVector(Box::new(Ty1 { name: ident, langle: generic.lt_token, inner, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 3d67a0a..13cbfcf 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -14,7 +14,7 @@ impl ToTokens for Type { } ident.to_tokens(tokens); } - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => { ty.to_tokens(tokens) } Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens), @@ -35,10 +35,12 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { - // Do not add cxx namespace to Vector since we're defining it in the user crate - if self.name == "UniquePtr" || self.name == "RustVec" { - let span = self.name.span(); + let span = self.name.span(); + let name = self.name.to_string(); + if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); + } else if name == "Vec" { + tokens.extend(quote_spanned!(span=> ::std::vec::)); } self.name.to_tokens(tokens); self.langle.to_tokens(tokens); diff --git a/syntax/typename.rs b/syntax/typename.rs deleted file mode 100644 index 883e1fb..0000000 --- a/syntax/typename.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::syntax::{Atom, Type}; - -pub trait ToTypename { - fn to_typename(&self, namespace: &Vec) -> String; -} - -impl ToTypename for Type { - fn to_typename(&self, namespace: &Vec) -> String { - match self { - Type::Ident(ident) => { - let mut inner = String::new(); - // Do not apply namespace to built-in type - let is_user_type = Atom::from(ident).is_none(); - if is_user_type { - for name in namespace { - inner += name; - inner += "::"; - } - } - if let Some(ti) = Atom::from(ident) { - inner += ti.to_cxx(); - } else { - inner += &ident.to_string(); - }; - inner - } - Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)), - Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)), - Type::UniquePtr(ptr) => { - format!("std::unique_ptr<{}>", ptr.inner.to_typename(namespace)) - } - Type::Vector(ptr) => format!("std::vector<{}>", ptr.inner.to_typename(namespace)), - _ => unimplemented!(), - } - } -} diff --git a/syntax/types.rs b/syntax/types.rs index 7bce154..f3aaa16 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -24,9 +24,10 @@ impl<'a> Types<'a> { all.insert(ty); match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => { - visit(all, &ty.inner) - } + Type::RustBox(ty) + | Type::UniquePtr(ty) + | Type::CxxVector(ty) + | Type::RustVec(ty) => visit(all, &ty.inner), Type::Ref(r) => visit(all, &r.inner), Type::Slice(s) => visit(all, &s.inner), Type::Fn(f) => { @@ -95,6 +96,7 @@ impl<'a> Types<'a> { Atom::from(ident) == Some(RustString) } } + Type::RustVec(_) => true, _ => false, } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index da0040a..d2c860e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -27,9 +27,13 @@ pub mod ffi { fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; fn c_return_unique_ptr_string() -> UniquePtr; - fn c_return_unique_ptr_vector_u8() -> UniquePtr>; - fn c_return_unique_ptr_vector_f64() -> UniquePtr>; - fn c_return_unique_ptr_vector_shared() -> UniquePtr>; + fn c_return_unique_ptr_vector_u8() -> UniquePtr>; + fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_shared() -> UniquePtr>; + fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; + fn c_return_ref_vector(c: &C) -> &CxxVector; + fn c_return_rust_vec() -> Vec; + fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -41,11 +45,13 @@ pub mod ffi { fn c_take_sliceu8(s: &[u8]); fn c_take_rust_string(s: String); fn c_take_unique_ptr_string(s: UniquePtr); - fn c_take_unique_ptr_vector_u8(s: UniquePtr>); - fn c_take_unique_ptr_vector_f64(s: UniquePtr>); - fn c_take_unique_ptr_vector_shared(s: UniquePtr>); - fn c_take_vec_u8(v: &Vec); - fn c_take_vec_shared(v: &Vec); + fn c_take_unique_ptr_vector_u8(v: UniquePtr>); + fn c_take_unique_ptr_vector_f64(v: UniquePtr>); + fn c_take_unique_ptr_vector_shared(v: UniquePtr>); + fn c_take_ref_vector(v: &CxxVector); + fn c_take_rust_vec(v: Vec); + fn c_take_rust_vec_shared(v: Vec); + fn c_take_ref_rust_vec(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; @@ -57,6 +63,8 @@ pub mod ffi { fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>; fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; + fn c_try_return_rust_vec() -> Result>; + fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec>; fn get(self: &C) -> usize; fn set(self: &mut C, n: usize) -> usize; @@ -76,6 +84,8 @@ pub mod ffi { fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; + fn r_return_rust_vec() -> Vec; + fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -87,6 +97,8 @@ pub mod ffi { fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + fn r_take_rust_vec(v: Vec); + fn r_take_ref_rust_vec(v: &Vec); fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; @@ -163,6 +175,15 @@ fn r_return_unique_ptr_string() -> UniquePtr { unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr_string()) } } +fn r_return_rust_vec() -> Vec { + Vec::new() +} + +fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { + let _ = shared; + unimplemented!() +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } @@ -204,6 +225,14 @@ fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } +fn r_take_rust_vec(v: Vec) { + let _ = v; +} + +fn r_take_ref_rust_vec(v: &Vec) { + let _ = v; +} + fn r_try_return_void() -> Result<(), Error> { Ok(()) } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index a8d7018..c0b5888 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -28,6 +28,8 @@ size_t C::set2(size_t n) { return this->n; } +const std::vector &C::get_v() const { return this->v; } + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -59,29 +61,45 @@ std::unique_ptr c_return_unique_ptr_string() { } std::unique_ptr> c_return_unique_ptr_vector_u8() { - auto retval = - std::unique_ptr>(new std::vector()); - retval->push_back(86); - retval->push_back(75); - retval->push_back(30); - retval->push_back(9); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(86); + vec->push_back(75); + vec->push_back(30); + vec->push_back(9); + return vec; } std::unique_ptr> c_return_unique_ptr_vector_f64() { - auto retval = std::unique_ptr>(new std::vector()); - retval->push_back(86.0); - retval->push_back(75.0); - retval->push_back(30.0); - retval->push_back(9.5); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(86.0); + vec->push_back(75.0); + vec->push_back(30.0); + vec->push_back(9.5); + return vec; } std::unique_ptr> c_return_unique_ptr_vector_shared() { - auto retval = std::unique_ptr>(new std::vector()); - retval->push_back(Shared{1010}); - retval->push_back(Shared{1011}); - return retval; + auto vec = std::unique_ptr>(new std::vector()); + vec->push_back(Shared{1010}); + vec->push_back(Shared{1011}); + return vec; +} + +std::unique_ptr> c_return_unique_ptr_vector_opaque() { + return std::unique_ptr>(new std::vector()); +} + +const std::vector &c_return_ref_vector(const C &c) { + return c.get_v(); +} + +rust::Vec c_return_rust_vec() { + throw std::runtime_error("unimplemented"); +} + +const rust::Vec &c_return_ref_rust_vec(const C &c) { + (void)c; + throw std::runtime_error("unimplemented"); } void c_take_primitive(size_t n) { @@ -163,18 +181,17 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { } } -void c_take_vec_u8(const ::rust::Vec &v) { - auto cv = static_cast>(v); - uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0); - if (sum == 200) { +void c_take_ref_vector(const std::vector &v) { + if (v.size() == 4) { cxx_test_suite_set_correct(); } } -void c_take_vec_shared(const ::rust::Vec &v) { - auto cv = static_cast>(v); +void c_take_rust_vec(rust::Vec v) { c_take_ref_rust_vec(v); } + +void c_take_rust_vec_shared(rust::Vec v) { uint32_t sum = 0; - for (auto i : cv) { + for (auto i : v) { sum += i.z; } if (sum == 2021) { @@ -182,6 +199,13 @@ void c_take_vec_shared(const ::rust::Vec &v) { } } +void c_take_ref_rust_vec(const rust::Vec &v) { + uint8_t sum = std::accumulate(v.begin(), v.end(), 0); + if (sum == 200) { + cxx_test_suite_set_correct(); + } +} + void c_take_callback(rust::Fn callback) { callback("2020"); } @@ -206,6 +230,15 @@ std::unique_ptr c_try_return_unique_ptr_string() { return c_return_unique_ptr_string(); } +rust::Vec c_try_return_rust_vec() { + throw std::runtime_error("unimplemented"); +} + +const rust::Vec &c_try_return_ref_rust_vec(const C &c) { + (void)c; + throw std::runtime_error("unimplemented"); +} + extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 876e3c5..1d617ab 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -15,9 +15,11 @@ public: size_t set(size_t n); size_t get2() const; size_t set2(size_t n); + const std::vector &get_v() const; private: size_t n; + std::vector v; }; size_t c_return_primitive(); @@ -32,6 +34,10 @@ std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); +std::unique_ptr> c_return_unique_ptr_vector_opaque(); +const std::vector &c_return_ref_vector(const C &c); +rust::Vec c_return_rust_vec(); +const rust::Vec &c_return_ref_rust_vec(const C &c); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -46,8 +52,10 @@ void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); -void c_take_vec_u8(const ::rust::Vec &v); -void c_take_vec_shared(const ::rust::Vec &v); +void c_take_ref_vector(const std::vector &v); +void c_take_rust_vec(rust::Vec v); +void c_take_rust_vec_shared(rust::Vec v); +void c_take_ref_rust_vec(const rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); @@ -59,5 +67,7 @@ rust::Str c_try_return_str(rust::Str); rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); +rust::Vec c_try_return_rust_vec(); +const rust::Vec &c_try_return_ref_rust_vec(const C &c); } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index b33c675..07c23bd 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -32,44 +32,22 @@ fn test_c_return() { assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); - assert_eq!( - 4, - ffi::c_return_unique_ptr_vector_u8() - .as_ref() - .unwrap() - .size() - ); + assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len()); assert_eq!( 200_u8, - ffi::c_return_unique_ptr_vector_u8() - .as_ref() - .unwrap() - .into_iter() - .sum() + ffi::c_return_unique_ptr_vector_u8().into_iter().sum(), ); assert_eq!( 200.5_f64, - ffi::c_return_unique_ptr_vector_f64() - .as_ref() - .unwrap() - .into_iter() - .sum() - ); - assert_eq!( - 2, - ffi::c_return_unique_ptr_vector_shared() - .as_ref() - .unwrap() - .size() + ffi::c_return_unique_ptr_vector_f64().into_iter().sum(), ); + assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len()); assert_eq!( 2021_usize, ffi::c_return_unique_ptr_vector_shared() - .as_ref() - .unwrap() .into_iter() .map(|o| o.z) - .sum() + .sum(), ); } @@ -113,11 +91,15 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_vector_shared( ffi::c_return_unique_ptr_vector_shared() )); - check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec())); - check!(ffi::c_take_vec_shared(&vec![ + check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); + check!(ffi::c_take_rust_vec([86_u8, 75_u8, 30_u8, 9_u8].to_vec())); + check!(ffi::c_take_rust_vec_shared(vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } ])); + check!(ffi::c_take_ref_rust_vec( + &[86_u8, 75_u8, 30_u8, 9_u8].to_vec() + )); } #[test] From a8df0943f5713622e79512dda87fe7e01a5782d8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:08:40 +0000 Subject: [PATCH 462/2232] Document pass-by-value restriction of CxxVector --- diff --git a/README.md b/README.md index f967762..121abbe 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ returns of functions. - +
name in Rustname in C++
Vec<T>tbd
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
tbdstd::vector<T>
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
tbdstd::shared_ptr<T>
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot hold opaque Rust type
CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
diff --git a/src/lib.rs b/src/lib.rs index e4ef8f2..51783dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -312,7 +312,7 @@ //! Box<T>rust::Box<T>cannot hold opaque C++ type //! UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type //! Vec<T>rust::Vec<T>cannot hold opaque C++ type -//! CxxVector<T>std::vector<T>cannot hold opaque Rust type +//! CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type //! fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far //! Result<T>throw/catchallowed as return type only //! From 8b6ffa6ab1099704e09e5ca0d3755167527622c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:17:17 +0000 Subject: [PATCH 463/2232] Fix mutability of push_back trait method --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index be58cbd..c404e00 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -693,10 +693,10 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { } &*__get_unchecked(v, pos) } - fn __push_back(v: &::cxx::CxxVector, item: &Self) { + fn __push_back(v: &mut ::cxx::CxxVector, item: &Self) { extern "C" { #[link_name = #link_push_back] - fn __push_back(_: &::cxx::CxxVector<#elem>, _: &#elem); + fn __push_back(_: &mut ::cxx::CxxVector<#elem>, _: &#elem); } unsafe { __push_back(v, item) } } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 8294044..77e7af1 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -126,7 +126,7 @@ pub unsafe trait VectorElement: Sized { const __NAME: &'static dyn Display; fn __vector_size(v: &CxxVector) -> usize; unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; - fn __push_back(v: &CxxVector, item: &Self); + fn __push_back(v: &mut CxxVector, item: &Self); fn __unique_ptr_null() -> *mut c_void; unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; @@ -158,11 +158,11 @@ macro_rules! impl_vector_element_for_primitive { } &*__get_unchecked(v, pos) } - fn __push_back(v: &CxxVector<$ty>, item: &$ty) { + fn __push_back(v: &mut CxxVector<$ty>, item: &$ty) { extern "C" { attr! { #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")] - fn __push_back(_: &CxxVector<$ty>, _: &$ty); + fn __push_back(_: &mut CxxVector<$ty>, _: &$ty); } } unsafe { __push_back(v, item) } From 8d06f5b588e28524e89a1a8708e8ef4decd3854e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:17:42 +0000 Subject: [PATCH 464/2232] Remove CxxVector::push_back for now I'd like to give some more thought to how to expose this. Taking the value by reference and copying it into the vector is nice when T is an opaque C++ type but otherwise a pretty weird API from Rust. --- diff --git a/gen/write.rs b/gen/write.rs index cdbfc75..b39d978 100644 --- a/gen/write.rs +++ b/gen/write.rs @@ -1217,13 +1217,6 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: ); writeln!(out, " return s[pos];"); writeln!(out, "}}"); - writeln!( - out, - "void cxxbridge02$std$vector${}$push_back(::std::vector<{}> &s, const {} &item) noexcept {{", - instance, inner, inner - ); - writeln!(out, " s.push_back(item);"); - writeln!(out, "}}"); write_unique_ptr_common(out, vector_ty, types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index c404e00..27d75fe 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -668,7 +668,6 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { let prefix = format!("cxxbridge02$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let link_push_back = format!("{}push_back", prefix); let unique_ptr_prefix = format!("cxxbridge02$unique_ptr$std$vector${}{}$", namespace, elem); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); @@ -693,13 +692,6 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { } &*__get_unchecked(v, pos) } - fn __push_back(v: &mut ::cxx::CxxVector, item: &Self) { - extern "C" { - #[link_name = #link_push_back] - fn __push_back(_: &mut ::cxx::CxxVector<#elem>, _: &#elem); - } - unsafe { __push_back(v, item) } - } fn __unique_ptr_null() -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_unique_ptr_null] diff --git a/src/cxx.cc b/src/cxx.cc index 0ead3f0..8409772 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -208,10 +208,6 @@ void cxxbridge02$unique_ptr$std$string$drop( const std::vector &s, size_t pos) noexcept { \ return &s[pos]; \ } \ - void cxxbridge02$std$vector$##RUST_TYPE##$push_back( \ - std::vector &s, const CXX_TYPE &item) noexcept { \ - s.push_back(item); \ - } \ void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 77e7af1..695fcad 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -62,11 +62,6 @@ where pub unsafe fn get_unchecked(&self, pos: usize) -> &T { T::__get_unchecked(self, pos) } - - /// Appends an element to the back of the vector. - pub fn push_back(&mut self, item: &T) { - T::__push_back(self, item); - } } pub struct Iter<'a, T> { @@ -126,7 +121,6 @@ pub unsafe trait VectorElement: Sized { const __NAME: &'static dyn Display; fn __vector_size(v: &CxxVector) -> usize; unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; - fn __push_back(v: &mut CxxVector, item: &Self); fn __unique_ptr_null() -> *mut c_void; unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; @@ -158,15 +152,6 @@ macro_rules! impl_vector_element_for_primitive { } &*__get_unchecked(v, pos) } - fn __push_back(v: &mut CxxVector<$ty>, item: &$ty) { - extern "C" { - attr! { - #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")] - fn __push_back(_: &mut CxxVector<$ty>, _: &$ty); - } - } - unsafe { __push_back(v, item) } - } fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { From ad959f6f236d98b385f2c0e034d560cba33007ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:32:39 +0000 Subject: [PATCH 465/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 9696c97..d8e826f 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.17/src/**"]), + srcs = glob(["vendor/syn-1.0.18/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index a3db8b1..3a2ac5b 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.17/src/**"]), + srcs = glob(["vendor/syn-1.0.18/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 2e70499..c5d5302 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" dependencies = [ "proc-macro2", "quote", From 7b4e6570154f6cb75e6a7c3677326330dd280a58 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:33:35 +0000 Subject: [PATCH 466/2232] Release 0.2.11 --- diff --git a/Cargo.toml b/Cargo.toml index f497482..ad452fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.10" # remember to update html_root_url +version = "0.2.11" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.10", path = "macro" } +cxxbridge-macro = { version = "=0.2.11", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 6ec328e..e87e1cf 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.10" +version = "0.2.11" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index ca0e326..1dd11f5 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.10" +version = "0.2.11" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 51783dd..0e724e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.10")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.11")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index c5d5302..258e09c 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.10" +version = "0.2.11" dependencies = [ "anyhow", "cc", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.10" +version = "0.2.11" dependencies = [ "anyhow", "codespan-reporting", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.10" +version = "0.2.11" dependencies = [ "cxx", "proc-macro2", From 52509840c7aa5317914ec88bfe44c08bc8255072 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:46:49 +0000 Subject: [PATCH 467/2232] Replace thiserror dependency with handwritten impls A dependency wasn't carrying its weight for just two Error impls. --- diff --git a/BUCK b/BUCK index 4357dd6..50a9048 100644 --- a/BUCK +++ b/BUCK @@ -12,7 +12,6 @@ rust_library( "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", - "//third-party:thiserror", ], ) @@ -30,7 +29,6 @@ rust_binary( "//third-party:quote", "//third-party:structopt", "//third-party:syn", - "//third-party:thiserror", ], ) diff --git a/BUILD b/BUILD index 59ac9fc..2eea8c7 100644 --- a/BUILD +++ b/BUILD @@ -15,7 +15,6 @@ rust_library( "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", - "//third-party:thiserror", ], ) @@ -31,7 +30,6 @@ rust_binary( "//third-party:quote", "//third-party:structopt", "//third-party:syn", - "//third-party:thiserror", ], ) diff --git a/Cargo.toml b/Cargo.toml index ad452fe..1d6ede2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } -thiserror = "1.0" [build-dependencies] cc = "1.0.49" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index e87e1cf..9218478 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -21,7 +21,6 @@ proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" structopt = "0.3" syn = { version = "1.0", features = ["full"] } -thiserror = "1.0" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/error.rs b/gen/error.rs index ca78acb..2e8ecc4 100644 --- a/gen/error.rs +++ b/gen/error.rs @@ -1,15 +1,59 @@ -use crate::gen::Error; use crate::syntax; use anyhow::anyhow; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; use codespan_reporting::term::{self, Config}; -use std::io::Write; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io::{self, Write}; use std::ops::Range; use std::path::Path; use std::process; +pub(super) type Result = std::result::Result; + +#[derive(Debug)] +pub(super) enum Error { + NoBridgeMod, + OutOfLineMod, + Io(io::Error), + Syn(syn::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), + Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), + Error::Io(err) => err.fmt(f), + Error::Syn(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + Error::Syn(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: syn::Error) -> Self { + Error::Syn(err) + } +} + pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { match error { Error::Syn(syn_error) => { diff --git a/gen/mod.rs b/gen/mod.rs index 4c3a292..928c6ec 100644 --- a/gen/mod.rs +++ b/gen/mod.rs @@ -6,29 +6,13 @@ pub(super) mod include; pub(super) mod out; mod write; -use self::error::format_err; +use self::error::{format_err, Error, Result}; use crate::syntax::namespace::Namespace; use crate::syntax::{self, check, Types}; use quote::quote; use std::fs; -use std::io; use std::path::Path; use syn::{Attribute, File, Item}; -use thiserror::Error; - -pub(super) type Result = std::result::Result; - -#[derive(Error, Debug)] -pub(super) enum Error { - #[error("no #[cxx::bridge] module found")] - NoBridgeMod, - #[error("#[cxx::bridge] module must have inline contents")] - OutOfLineMod, - #[error(transparent)] - Io(#[from] io::Error), - #[error(transparent)] - Syn(#[from] syn::Error), -} struct Input { namespace: Namespace, diff --git a/src/error.rs b/src/error.rs index 0c19f98..740ab94 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,37 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; use std::io; -use thiserror::Error; pub(super) type Result = std::result::Result; -#[derive(Error, Debug)] +#[derive(Debug)] pub(super) enum Error { - #[error("missing OUT_DIR environment variable")] MissingOutDir, - #[error("failed to locate target dir")] TargetDir, - #[error(transparent)] - Io(#[from] io::Error), + Io(io::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), + Error::TargetDir => write!(f, "failed to locate target dir"), + Error::Io(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } } diff --git a/third-party/BUCK b/third-party/BUCK index d8e826f..54a1e85 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -170,24 +170,6 @@ rust_library( ) rust_library( - name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.15/src/**"]), - visibility = ["PUBLIC"], - deps = [":thiserror-impl"], -) - -rust_library( - name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.15/src/**"]), - proc_macro = True, - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "unicode-segmentation", srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), edition = "2015", diff --git a/third-party/BUILD b/third-party/BUILD index 3a2ac5b..c6ce473 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -175,24 +175,6 @@ rust_library( ) rust_library( - name = "thiserror", - srcs = glob(["vendor/thiserror-1.0.15/src/**"]), - visibility = ["//visibility:public"], - deps = [":thiserror-impl"], -) - -rust_library( - name = "thiserror-impl", - srcs = glob(["vendor/thiserror-impl-1.0.15/src/**"]), - crate_type = "proc-macro", - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "unicode-segmentation", srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), edition = "2015", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 258e09c..0fc8cd8 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -77,7 +77,6 @@ dependencies = [ "quote", "rustversion", "syn", - "thiserror", "trybuild", ] @@ -98,7 +97,6 @@ dependencies = [ "quote", "structopt", "syn", - "thiserror", ] [[package]] @@ -338,26 +336,6 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "toml" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" From e948983427ffb4369cabbeabba6b08a94c66bfa5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 02:50:45 +0000 Subject: [PATCH 468/2232] Ignore BUILD files under target directory After doing a `cargo publish` or `cargo package` we were getting a BUILD file at target/package/cxx-0.2.11/BUILD which was subsequently picked up by `bazel build ...`. --- diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +target/ From bd22ce57ddff10fb3060e4a2c2ed201098f2f013 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 26 2020 16:06:52 +0000 Subject: [PATCH 469/2232] Space out travis matrix One big chunk of yaml with entries of varying length was annoying to skim. --- diff --git a/.travis.yml b/.travis.yml index 124fc57..6c0ca54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ matrix: - name: macOS os: macos rust: nightly + - name: Windows (gnu) os: windows rust: nightly-x86_64-pc-windows-gnu @@ -21,12 +22,14 @@ matrix: # windows is bad at symlinks - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - name: Windows (msvc) os: windows rust: nightly-x86_64-pc-windows-msvc before_script: - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - name: Buck rust: nightly before_install: @@ -41,6 +44,7 @@ matrix: - buck build :cxx#check --verbose=0 - buck run demo-rs --verbose=0 - buck test ... --verbose=0 + - name: Bazel rust: nightly before_install: @@ -53,6 +57,7 @@ matrix: script: - bazel run demo-rs --verbose_failures --noshow_progress - bazel test ... --verbose_failures --noshow_progress + - name: Minimum rustc rust: 1.42.0 script: From ba67607fa20cdcbfe9e52a9586d9a669ba9c3280 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 27 2020 23:00:50 +0000 Subject: [PATCH 470/2232] Allow wildcard argument names. This adds support for wildcard variable names by internally giving them unique names. --- diff --git a/syntax/parse.rs b/syntax/parse.rs index d14806d..a425c56 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,9 +6,9 @@ use crate::syntax::{ use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, - TypeBareFn, TypePath, TypeReference, TypeSlice, + Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Ident, + Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, + Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -193,6 +193,9 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { Pat::Ident(pat) => pat.ident.clone(), + Pat::Wild(pat) => { + Ident::new(&format!("_{}", args.len()), pat.underscore_token.span) + } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; let ty = parse_type(&arg.ty)?; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d2c860e..69ef2b0 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -34,6 +34,8 @@ pub mod ffi { fn c_return_ref_vector(c: &C) -> &CxxVector; fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; + fn c_return_identity(_: usize) -> usize; + fn c_return_sum(_: usize, _: usize) -> usize; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -86,6 +88,8 @@ pub mod ffi { fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; + fn r_return_identity(_: usize) -> usize; + fn r_return_sum(_: usize, _: usize) -> usize; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -184,6 +188,14 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { unimplemented!() } +fn r_return_identity(n: usize) -> usize { + n +} + +fn r_return_sum(n1: usize, n2: usize) -> usize { + n1 + n2 +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index c0b5888..11096ba 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -102,6 +102,14 @@ const rust::Vec &c_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } +size_t c_return_identity(size_t n) { + return n; +} + +size_t c_return_sum(size_t n1, size_t n2) { + return n1 + n2; +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -265,6 +273,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); ASSERT(*r_return_unique_ptr_string() == "2020"); + ASSERT(r_return_identity(2020) == 2020); + ASSERT(r_return_sum(2020, 1) == 2021); r_take_primitive(2020); r_take_shared(Shared{2020}); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 1d617ab..4600d64 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -38,6 +38,8 @@ std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); +size_t c_return_identity(size_t n); +size_t c_return_sum(size_t n1, size_t n2); void c_take_primitive(size_t n); void c_take_shared(Shared shared); diff --git a/tests/test.rs b/tests/test.rs index 07c23bd..4fd52d3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -49,6 +49,8 @@ fn test_c_return() { .map(|o| o.z) .sum(), ); + assert_eq!(2020, ffi::c_return_identity(2020)); + assert_eq!(2021, ffi::c_return_sum(2020, 1)); } #[test] From d6c521916f2893ceaecfe4a54f5fda503a6ec0f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 27 2020 23:46:22 +0000 Subject: [PATCH 471/2232] Merge pull request #159 from jgalenson/underscore Allow wildcard argument names. --- diff --git a/syntax/parse.rs b/syntax/parse.rs index d14806d..a425c56 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,9 +6,9 @@ use crate::syntax::{ use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item, - ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, - TypeBareFn, TypePath, TypeReference, TypeSlice, + Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Ident, + Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, + Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -193,6 +193,9 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { FnArg::Typed(arg) => { let ident = match arg.pat.as_ref() { Pat::Ident(pat) => pat.ident.clone(), + Pat::Wild(pat) => { + Ident::new(&format!("_{}", args.len()), pat.underscore_token.span) + } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; let ty = parse_type(&arg.ty)?; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d2c860e..69ef2b0 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -34,6 +34,8 @@ pub mod ffi { fn c_return_ref_vector(c: &C) -> &CxxVector; fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; + fn c_return_identity(_: usize) -> usize; + fn c_return_sum(_: usize, _: usize) -> usize; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -86,6 +88,8 @@ pub mod ffi { fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; + fn r_return_identity(_: usize) -> usize; + fn r_return_sum(_: usize, _: usize) -> usize; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -184,6 +188,14 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { unimplemented!() } +fn r_return_identity(n: usize) -> usize { + n +} + +fn r_return_sum(n1: usize, n2: usize) -> usize { + n1 + n2 +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index c0b5888..11096ba 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -102,6 +102,14 @@ const rust::Vec &c_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } +size_t c_return_identity(size_t n) { + return n; +} + +size_t c_return_sum(size_t n1, size_t n2) { + return n1 + n2; +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -265,6 +273,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); ASSERT(*r_return_unique_ptr_string() == "2020"); + ASSERT(r_return_identity(2020) == 2020); + ASSERT(r_return_sum(2020, 1) == 2021); r_take_primitive(2020); r_take_shared(Shared{2020}); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 1d617ab..4600d64 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -38,6 +38,8 @@ std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); +size_t c_return_identity(size_t n); +size_t c_return_sum(size_t n1, size_t n2); void c_take_primitive(size_t n); void c_take_shared(Shared shared); diff --git a/tests/test.rs b/tests/test.rs index 07c23bd..4fd52d3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -49,6 +49,8 @@ fn test_c_return() { .map(|o| o.z) .sum(), ); + assert_eq!(2020, ffi::c_return_identity(2020)); + assert_eq!(2021, ffi::c_return_sum(2020, 1)); } #[test] From 378ca50c4c61ce15a23cf2080c7cdb41cdaa2cb5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 27 2020 23:47:55 +0000 Subject: [PATCH 472/2232] Format PR 159 with clang-format --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 11096ba..79db42c 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -102,13 +102,9 @@ const rust::Vec &c_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } -size_t c_return_identity(size_t n) { - return n; -} +size_t c_return_identity(size_t n) { return n; } -size_t c_return_sum(size_t n1, size_t n2) { - return n1 + n2; -} +size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } void c_take_primitive(size_t n) { if (n == 2020) { From 4f3b6fd2e988478f96755250446792bb407136a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 00:10:12 +0000 Subject: [PATCH 473/2232] Lockfile update --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 0fc8cd8..70ae2cc 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" dependencies = [ "libc", ] @@ -346,9 +346,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459186ab1afd6d93bd23c2269125f4f7694f8771fe0e64434b4bdc212b94034d" +checksum = "4e5696e4fd793743fbcc29943fe965ea3993b6c3d2a6a3a35c6680d926fd3a49" dependencies = [ "dissimilar", "glob", From 4fc533190ae771039fa5b4546767a06cbd6b502e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 00:10:39 +0000 Subject: [PATCH 474/2232] Release 0.2.12 --- diff --git a/Cargo.toml b/Cargo.toml index 1d6ede2..a47a842 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.11" # remember to update html_root_url +version = "0.2.12" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge02" @@ -18,7 +18,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -cxxbridge-macro = { version = "=0.2.11", path = "macro" } +cxxbridge-macro = { version = "=0.2.12", path = "macro" } link-cplusplus = "1.0" proc-macro2 = { version = "1.0", features = ["span-locations"] } quote = "1.0" diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml index 9218478..742d54b 100644 --- a/cmd/Cargo.toml +++ b/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.11" +version = "0.2.12" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 1dd11f5..82401ee 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.11" +version = "0.2.12" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 0e724e1..cff1e88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.11")] +#![doc(html_root_url = "https://docs.rs/cxx/0.2.12")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 70ae2cc..0bd470b 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.11" +version = "0.2.12" dependencies = [ "anyhow", "cc", @@ -89,7 +89,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.11" +version = "0.2.12" dependencies = [ "anyhow", "codespan-reporting", @@ -108,7 +108,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.11" +version = "0.2.12" dependencies = [ "cxx", "proc-macro2", From 5d08baa8ae0c5473358e895bbd0a332ee9f0a620 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 01:12:08 +0000 Subject: [PATCH 475/2232] Link to release notes from readme --- diff --git a/README.md b/README.md index 121abbe..7dc7c8b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ can be 100% safe. cxx = "0.2" ``` -*Compiler support: requires rustc 1.42+* +*Compiler support: requires rustc 1.42+*
+*[Release notes](https://github.com/dtolnay/cxx/releases)*
From a53c90f08a56fdf6d9da202282ac9bd370129c63 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 01:21:52 +0000 Subject: [PATCH 476/2232] Merge pull request #160 from dtolnay/relnotes Link to release notes from readme --- diff --git a/README.md b/README.md index 121abbe..7dc7c8b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ can be 100% safe. cxx = "0.2" ``` -*Compiler support: requires rustc 1.42+* +*Compiler support: requires rustc 1.42+*
+*[Release notes](https://github.com/dtolnay/cxx/releases)*
From 2bd1312f2f1b942c671b20769c9f247b35cc1ef1 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 28 2020 17:14:41 +0000 Subject: [PATCH 477/2232] Disallow passing a reference to a reference. --- diff --git a/syntax/check.rs b/syntax/check.rs index 0c75de8..a90db49 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -230,6 +230,11 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { ); } } + if let Type::Ref(ity) = &arg.ty { + if let Type::Ref(_) = &ity.inner { + cx.error(arg, "Passing a reference to a reference is not supported"); + } + } } if let Some(ty) = &efn.ret { diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs new file mode 100644 index 0000000..526d036 --- /dev/null +++ b/tests/ui/reference_to_reference.rs @@ -0,0 +1,13 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type ThingC; + fn repro_c(t: &&ThingC); + } + extern "Rust" { + type ThingR; + fn repro_r(t: &&ThingR); + } +} + +fn main() {} diff --git a/tests/ui/reference_to_reference.stderr b/tests/ui/reference_to_reference.stderr new file mode 100644 index 0000000..5b3eadc --- /dev/null +++ b/tests/ui/reference_to_reference.stderr @@ -0,0 +1,11 @@ +error: Passing a reference to a reference is not supported + --> $DIR/reference_to_reference.rs:5:20 + | +5 | fn repro_c(t: &&ThingC); + | ^^^^^^^^^^^ + +error: Passing a reference to a reference is not supported + --> $DIR/reference_to_reference.rs:9:20 + | +9 | fn repro_r(t: &&ThingR); + | ^^^^^^^^^^^ From f6a7167ea99d85cb691bfcb543271931a6111d85 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 19:32:01 +0000 Subject: [PATCH 478/2232] Merge pull request #162 from jgalenson/refref Disallow passing a reference to a reference. --- diff --git a/syntax/check.rs b/syntax/check.rs index 0c75de8..a90db49 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -230,6 +230,11 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { ); } } + if let Type::Ref(ity) = &arg.ty { + if let Type::Ref(_) = &ity.inner { + cx.error(arg, "Passing a reference to a reference is not supported"); + } + } } if let Some(ty) = &efn.ret { diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs new file mode 100644 index 0000000..526d036 --- /dev/null +++ b/tests/ui/reference_to_reference.rs @@ -0,0 +1,13 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + type ThingC; + fn repro_c(t: &&ThingC); + } + extern "Rust" { + type ThingR; + fn repro_r(t: &&ThingR); + } +} + +fn main() {} diff --git a/tests/ui/reference_to_reference.stderr b/tests/ui/reference_to_reference.stderr new file mode 100644 index 0000000..5b3eadc --- /dev/null +++ b/tests/ui/reference_to_reference.stderr @@ -0,0 +1,11 @@ +error: Passing a reference to a reference is not supported + --> $DIR/reference_to_reference.rs:5:20 + | +5 | fn repro_c(t: &&ThingC); + | ^^^^^^^^^^^ + +error: Passing a reference to a reference is not supported + --> $DIR/reference_to_reference.rs:9:20 + | +9 | fn repro_r(t: &&ThingR); + | ^^^^^^^^^^^ From 776fd8953dbafa9b06a333540fb51fce91a3de94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 28 2020 20:38:28 +0000 Subject: [PATCH 479/2232] Generalize reference-to-reference check to cover all positions Checking this in check_type_ref allows it to apply anywhere that a reference is written, such as return position which was not covered by the previous logic. --- diff --git a/syntax/check.rs b/syntax/check.rs index a90db49..3e121b5 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -151,6 +151,10 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) { match ty.inner { Type::Fn(_) | Type::Void(_) => {} + Type::Ref(_) => { + cx.error(ty, "C++ does not allow references to references"); + return; + } _ => return, } @@ -230,11 +234,6 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { ); } } - if let Type::Ref(ity) = &arg.ty { - if let Type::Ref(_) = &ity.inner { - cx.error(arg, "Passing a reference to a reference is not supported"); - } - } } if let Some(ty) = &efn.ret { diff --git a/tests/ui/reference_to_reference.stderr b/tests/ui/reference_to_reference.stderr index 5b3eadc..d4069c1 100644 --- a/tests/ui/reference_to_reference.stderr +++ b/tests/ui/reference_to_reference.stderr @@ -1,11 +1,11 @@ -error: Passing a reference to a reference is not supported - --> $DIR/reference_to_reference.rs:5:20 +error: C++ does not allow references to references + --> $DIR/reference_to_reference.rs:5:23 | 5 | fn repro_c(t: &&ThingC); - | ^^^^^^^^^^^ + | ^^^^^^^^ -error: Passing a reference to a reference is not supported - --> $DIR/reference_to_reference.rs:9:20 +error: C++ does not allow references to references + --> $DIR/reference_to_reference.rs:9:23 | 9 | fn repro_r(t: &&ThingR); - | ^^^^^^^^^^^ + | ^^^^^^^^ From da9be50ad868b4ba367e6197a9fc81499303fc76 Mon Sep 17 00:00:00 2001 From: myronahn Date: Apr 28 2020 22:47:23 +0000 Subject: [PATCH 480/2232] Add all std::iterator_traits required types to const_iterator of rust::Vec (#157) https://en.cppreference.com/w/cpp/iterator/iterator_traits `std::iterator_traits` requires the following 5 types: - `difference_type` - a signed integer type that can be used to identify distance between iterators - `value_type` - the type of the values that can be obtained by dereferencing the iterator. This type is void for output iterators. - `pointer` - defines a pointer to the type iterated over (value_type) - `reference` - defines a reference to the type iterated over (value_type) - `iterator_category` - the category of the iterator. Must be one of iterator category tags. The current `const_iterator` for `rust::Vec` only defined `value_type` and `reference` which caused an error when using `std::copy` on gcc 7.5.0 on Ubuntu but seemed to work fine on MacOS. --- diff --git a/include/cxx.h b/include/cxx.h index c02d3d9..1e33618 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -235,15 +235,25 @@ public: class const_iterator { public: + using difference_type = ptrdiff_t; using value_type = typename std::add_const::type; + using pointer = typename std::add_pointer< + typename std::add_const::type>::type; using reference = typename std::add_lvalue_reference< typename std::add_const::type>::type; + using iterator_category = std::forward_iterator_tag; const T &operator*() const { return *static_cast(this->pos); } + const T *operator->() const { return static_cast(this->pos); } const_iterator &operator++() { this->pos = static_cast(this->pos) + this->stride; return *this; } + const_iterator operator++(int) { + auto ret = const_iterator(*this); + this->pos = static_cast(this->pos) + this->stride; + return ret; + } bool operator==(const const_iterator &other) const { return this->pos == other.pos; } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 69ef2b0..01dc404 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -53,7 +53,9 @@ pub mod ffi { fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); fn c_take_rust_vec_shared(v: Vec); + fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); + fn c_take_ref_rust_vec_copy(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); fn c_try_return_void() -> Result<()>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 79db42c..2b5431f 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -203,6 +203,18 @@ void c_take_rust_vec_shared(rust::Vec v) { } } +void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { + // Exercise requirements of ForwardIterator + // https://en.cppreference.com/w/cpp/named_req/ForwardIterator + uint32_t sum = 0; + for (auto it = v.begin(), it_end = v.end(); it != it_end; it++) { + sum += it->z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_rust_vec(const rust::Vec &v) { uint8_t sum = std::accumulate(v.begin(), v.end(), 0); if (sum == 200) { @@ -210,6 +222,18 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } +void c_take_ref_rust_vec_copy(const rust::Vec &v) { + // The std::copy() will make sure rust::Vec<>::const_iterator satisfies the + // requirements for std::iterator_traits. + // https://en.cppreference.com/w/cpp/iterator/iterator_traits + std::vector cxx_v; + std::copy(v.begin(), v.end(), back_inserter(cxx_v)); + uint8_t sum = std::accumulate(cxx_v.begin(), cxx_v.end(), 0); + if (sum == 200) { + cxx_test_suite_set_correct(); + } +} + void c_take_callback(rust::Fn callback) { callback("2020"); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 4600d64..da8ca56 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -57,7 +57,9 @@ void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); +void c_take_ref_rust_vec_copy(const rust::Vec &v); void c_take_callback(rust::Fn callback); void c_try_return_void(); diff --git a/tests/test.rs b/tests/test.rs index 4fd52d3..d6850aa 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -94,14 +94,18 @@ fn test_c_take() { ffi::c_return_unique_ptr_vector_shared() )); check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); - check!(ffi::c_take_rust_vec([86_u8, 75_u8, 30_u8, 9_u8].to_vec())); + let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec(); + check!(ffi::c_take_rust_vec(test_vec.clone())); check!(ffi::c_take_rust_vec_shared(vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } ])); - check!(ffi::c_take_ref_rust_vec( - &[86_u8, 75_u8, 30_u8, 9_u8].to_vec() - )); + check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![ + ffi::Shared { z: 1010 }, + ffi::Shared { z: 1011 } + ])); + check!(ffi::c_take_ref_rust_vec(&test_vec)); + check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); } #[test] From 6ba5ccf4c34e78af21684500515d91eeb86b6cbc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 29 2020 23:33:19 +0000 Subject: [PATCH 481/2232] Remove cargo author var from BUCK target This was made unnecessary by 7f635f360e279d3c3a00f78a8911cca32d90991a. --- diff --git a/BUCK b/BUCK index 50a9048..40aeea6 100644 --- a/BUCK +++ b/BUCK @@ -19,9 +19,6 @@ rust_binary( name = "codegen", srcs = glob(["cmd/src/**"]), visibility = ["PUBLIC"], - env = { - "CARGO_PKG_AUTHORS": "David Tolnay ", - }, deps = [ "//third-party:anyhow", "//third-party:codespan-reporting", From 1f5670249047033796d6840255113e18b385d1e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 29 2020 23:38:36 +0000 Subject: [PATCH 482/2232] Set binary name for buck-built codegen cli --- diff --git a/BUCK b/BUCK index 40aeea6..6a9870e 100644 --- a/BUCK +++ b/BUCK @@ -18,6 +18,7 @@ rust_library( rust_binary( name = "codegen", srcs = glob(["cmd/src/**"]), + crate = "cxxbridge", visibility = ["PUBLIC"], deps = [ "//third-party:anyhow", From f8ed07327b5217c3e63b44597dc026f95156bdbd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:23:14 +0000 Subject: [PATCH 483/2232] Split cxx runtime and build components --- diff --git a/.travis.yml b/.travis.yml index 6c0ca54..f65f7f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,15 +20,15 @@ matrix: rust: nightly-x86_64-pc-windows-gnu before_script: # windows is bad at symlinks - - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax + - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - name: Windows (msvc) os: windows rust: nightly-x86_64-pc-windows-msvc before_script: - - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax + - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - name: Buck rust: nightly diff --git a/BUCK b/BUCK index 6a9870e..2339f71 100644 --- a/BUCK +++ b/BUCK @@ -5,19 +5,13 @@ rust_library( deps = [ ":core", ":macro", - "//third-party:anyhow", - "//third-party:cc", - "//third-party:codespan-reporting", "//third-party:link-cplusplus", - "//third-party:proc-macro2", - "//third-party:quote", - "//third-party:syn", ], ) rust_binary( name = "codegen", - srcs = glob(["cmd/src/**"]), + srcs = glob(["gen/cmd/src/**"]), crate = "cxxbridge", visibility = ["PUBLIC"], deps = [ @@ -52,3 +46,17 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "build", + srcs = glob(["gen/build/src/**"]), + visibility = ["PUBLIC"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/BUILD b/BUILD index 2eea8c7..d63fea7 100644 --- a/BUILD +++ b/BUILD @@ -3,25 +3,18 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), - data = ["src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ ":core-lib", ":cxxbridge-macro", - "//third-party:anyhow", - "//third-party:cc", - "//third-party:codespan-reporting", "//third-party:link-cplusplus", - "//third-party:proc-macro2", - "//third-party:quote", - "//third-party:syn", ], ) rust_binary( name = "codegen", - srcs = glob(["cmd/src/**/*.rs"]), - data = ["cmd/src/gen/include/cxx.h"], + srcs = glob(["gen/cmd/src/**/*.rs"]), + data = ["gen/cmd/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", @@ -57,3 +50,18 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "build", + srcs = glob(["gen/build/src/**/*.rs"]), + data = ["gen/build/src/gen/include/cxx.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/Cargo.toml b/Cargo.toml index a47a842..ea97712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,25 +15,20 @@ exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] travis-ci = { repository = "dtolnay/cxx" } [dependencies] -anyhow = "1.0" -cc = "1.0.49" -codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.12", path = "macro" } link-cplusplus = "1.0" -proc-macro2 = { version = "1.0", features = ["span-locations"] } -quote = "1.0" -syn = { version = "1.0", features = ["full"] } [build-dependencies] cc = "1.0.49" [dev-dependencies] +cxx-build = { version = "=0.2.12", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.21", features = ["diff"] } [workspace] -members = ["cmd", "demo-rs", "macro", "tests/ffi"] +members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/README.md b/README.md index 7dc7c8b..59bcdac 100644 --- a/README.md +++ b/README.md @@ -219,8 +219,7 @@ set up any additional source files and compiler flags as normal. // build.rs fn main() { - cxx::Build::new() - .bridge("src/main.rs") // returns a cc::Build + cxx_build::bridge("src/main.rs") // returns a cc::Build .file("../demo-cxx/demo.cc") .flag("-std=c++11") .compile("cxxbridge-demo"); diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml deleted file mode 100644 index 742d54b..0000000 --- a/cmd/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "cxxbridge-cmd" -version = "0.2.12" -authors = ["David Tolnay "] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." -repository = "https://github.com/dtolnay/cxx" - -[[bin]] -name = "cxxbridge" -path = "src/main.rs" - -[badges] -travis-ci = { repository = "dtolnay/cxx" } - -[dependencies] -anyhow = "1.0" -codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } -quote = "1.0" -structopt = "0.3" -syn = { version = "1.0", features = ["full"] } - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] diff --git a/cmd/LICENSE-APACHE b/cmd/LICENSE-APACHE deleted file mode 120000 index 965b606..0000000 --- a/cmd/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/cmd/LICENSE-MIT b/cmd/LICENSE-MIT deleted file mode 120000 index 76219eb..0000000 --- a/cmd/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/cmd/src/gen b/cmd/src/gen deleted file mode 120000 index eb22577..0000000 --- a/cmd/src/gen +++ /dev/null @@ -1 +0,0 @@ -../../gen \ No newline at end of file diff --git a/cmd/src/lib.rs b/cmd/src/lib.rs deleted file mode 100644 index 8b1a393..0000000 --- a/cmd/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// empty diff --git a/cmd/src/main.rs b/cmd/src/main.rs deleted file mode 100644 index a20179f..0000000 --- a/cmd/src/main.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![allow( - clippy::cognitive_complexity, - clippy::inherent_to_string, - clippy::large_enum_variant, - clippy::new_without_default, - clippy::toplevel_ref_arg -)] - -mod gen; -mod syntax; - -use gen::include; -use std::io::{self, Write}; -use std::path::PathBuf; -use structopt::StructOpt; - -#[derive(StructOpt, Debug)] -#[structopt( - name = "cxxbridge", - author = "David Tolnay ", - about = "https://github.com/dtolnay/cxx", - usage = "\ - cxxbridge .rs Emit .cc file for bridge to stdout - cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout", - help_message = "Print help information", - version_message = "Print version information" -)] -struct Opt { - /// Input Rust source file containing #[cxx::bridge] - #[structopt(parse(from_os_str), required_unless = "header")] - input: Option, - - /// Emit header with declarations only - #[structopt(long)] - header: bool, - - /// Any additional headers to #include - #[structopt(short, long)] - include: Vec, -} - -fn write(content: impl AsRef<[u8]>) { - let _ = io::stdout().lock().write_all(content.as_ref()); -} - -fn main() { - let opt = Opt::from_args(); - - let gen = gen::Opt { - include: opt.include, - }; - - match (opt.input, opt.header) { - (Some(input), true) => write(gen::do_generate_header(&input, gen)), - (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), - (None, true) => write(include::HEADER), - (None, false) => unreachable!(), // enforced by required_unless - } -} diff --git a/cmd/src/syntax b/cmd/src/syntax deleted file mode 120000 index 83b0080..0000000 --- a/cmd/src/syntax +++ /dev/null @@ -1 +0,0 @@ -../../syntax \ No newline at end of file diff --git a/demo-rs/Cargo.toml b/demo-rs/Cargo.toml index f7e7f84..d2147ab 100644 --- a/demo-rs/Cargo.toml +++ b/demo-rs/Cargo.toml @@ -9,4 +9,4 @@ publish = false cxx = { path = ".." } [build-dependencies] -cxx = { path = ".." } +cxx-build = { path = "../gen/build" } diff --git a/demo-rs/build.rs b/demo-rs/build.rs index 71def71..edbb281 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,6 +1,5 @@ fn main() { - cxx::Build::new() - .bridge("src/main.rs") + cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") .flag("-std=c++11") .compile("cxxbridge-demo"); diff --git a/gen/README.md b/gen/README.md new file mode 100644 index 0000000..9786911 --- /dev/null +++ b/gen/README.md @@ -0,0 +1,4 @@ +This directory contains CXX's C++ code generator. This code generator has two +public frontends, one a command-line application (binary) in the *cmd* directory +and the other a library intended to be used from a build.rs in the *build* +directory. diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml new file mode 100644 index 0000000..3552524 --- /dev/null +++ b/gen/build/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cxx-build" +version = "0.2.12" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into a Cargo build." +repository = "https://github.com/dtolnay/cxx" + +[badges] +travis-ci = { repository = "dtolnay/cxx" } + +[dependencies] +anyhow = "1.0" +cc = "1.0.49" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0", features = ["span-locations"] } +quote = "1.0" +syn = { version = "1.0", features = ["full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/build/LICENSE-APACHE b/gen/build/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/build/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/build/LICENSE-MIT b/gen/build/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/build/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs new file mode 100644 index 0000000..740ab94 --- /dev/null +++ b/gen/build/src/error.rs @@ -0,0 +1,37 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +pub(super) type Result = std::result::Result; + +#[derive(Debug)] +pub(super) enum Error { + MissingOutDir, + TargetDir, + Io(io::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), + Error::TargetDir => write!(f, "failed to locate target dir"), + Error::Io(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} diff --git a/gen/build/src/gen b/gen/build/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/build/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs new file mode 100644 index 0000000..5bee67f --- /dev/null +++ b/gen/build/src/lib.rs @@ -0,0 +1,96 @@ +//! The CXX code generator for constructing and compiling C++ code. +//! +//! This is intended to be used from Cargo build scripts to execute CXX's +//! C++ code generator, set up any additional compiler flags depending on +//! the use case, and make the C++ compiler invocation. +//! +//!
+//! +//! # Example +//! +//! Example of a canonical Cargo build script that builds a CXX bridge: +//! +//! ```no_run +//! // build.rs +//! +//! fn main() { +//! cxx_build::bridge("src/main.rs") +//! .file("../demo-cxx/demo.cc") +//! .flag("-std=c++11") +//! .compile("cxxbridge-demo"); +//! +//! println!("cargo:rerun-if-changed=src/main.rs"); +//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); +//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! } +//! ``` +//! +//! A runnable working setup with this build script is shown in the +//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. +//! +//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx +//! +//!
+//! +//! # Alternatives +//! +//! For use in non-Cargo builds like Bazel or Buck, CXX provides an +//! alternate way of invoking the C++ code generator as a standalone command +//! line tool. The tool is packaged as the `cxxbridge-cmd` crate. +//! +//! ```bash +//! $ cargo install cxxbridge-cmd # or build it from the repo +//! +//! $ cxxbridge src/main.rs --header > path/to/mybridge.h +//! $ cxxbridge src/main.rs > path/to/mybridge.cc +//! ``` + +mod error; +mod gen; +mod paths; +mod syntax; + +use crate::error::Result; +use crate::gen::Opt; +use anyhow::anyhow; +use std::fs; +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +/// This returns a [`cc::Build`] on which you should continue to set up any +/// additional source files or compiler flags, and lastly call its [`compile`] +/// method to execute the C++ build. +/// +/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile +#[must_use] +pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { + match try_generate_bridge(rust_source_file.as_ref()) { + Ok(build) => build, + Err(err) => { + let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); + process::exit(1); + } + } +} + +fn try_generate_bridge(rust_source_file: &Path) -> Result { + let header = gen::do_generate_header(rust_source_file, Opt::default()); + let header_path = paths::out_with_extension(rust_source_file, ".h")?; + fs::create_dir_all(header_path.parent().unwrap())?; + fs::write(&header_path, header)?; + paths::symlink_header(&header_path, rust_source_file); + + let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); + let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; + fs::write(&bridge_path, bridge)?; + let mut build = paths::cc_build(); + build.file(&bridge_path); + + let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); + let _ = fs::create_dir_all(cxx_h.parent().unwrap()); + let _ = fs::remove_file(cxx_h); + let _ = fs::write(cxx_h, gen::include::HEADER); + + Ok(build) +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs new file mode 100644 index 0000000..ca183d9 --- /dev/null +++ b/gen/build/src/paths.rs @@ -0,0 +1,116 @@ +use crate::error::{Error, Result}; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +fn out_dir() -> Result { + env::var_os("OUT_DIR") + .map(PathBuf::from) + .ok_or(Error::MissingOutDir) +} + +pub(crate) fn cc_build() -> cc::Build { + try_cc_build().unwrap_or_default() +} + +fn try_cc_build() -> Result { + let mut build = cc::Build::new(); + build.include(include_dir()?); + build.include(target_dir()?.parent().unwrap()); + Ok(build) +} + +// Symlink the header file into a predictable place. The header generated from +// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. +pub(crate) fn symlink_header(path: &Path, original: &Path) { + let _ = try_symlink_header(path, original); +} + +fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { + let suffix = relative_to_parent_of_target_dir(original)?; + let ref dst = include_dir()?.join(suffix); + + fs::create_dir_all(dst.parent().unwrap())?; + let _ = fs::remove_file(dst); + symlink_or_copy(path, dst)?; + + let mut file_name = dst.file_name().unwrap().to_os_string(); + file_name.push(".h"); + let ref dst2 = dst.with_file_name(file_name); + symlink_or_copy(path, dst2)?; + + Ok(()) +} + +fn relative_to_parent_of_target_dir(original: &Path) -> Result { + let target_dir = target_dir()?; + let mut outer = target_dir.parent().unwrap(); + let original = canonicalize(original)?; + loop { + if let Ok(suffix) = original.strip_prefix(outer) { + return Ok(suffix.to_owned()); + } + match outer.parent() { + Some(parent) => outer = parent, + None => return Ok(original.components().skip(1).collect()), + } + } +} + +pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { + let mut file_name = path.file_name().unwrap().to_owned(); + file_name.push(ext); + + let out_dir = out_dir()?; + let rel = relative_to_parent_of_target_dir(path)?; + Ok(out_dir.join(rel).with_file_name(file_name)) +} + +pub(crate) fn include_dir() -> Result { + let target_dir = target_dir()?; + Ok(target_dir.join("cxxbridge")) +} + +fn target_dir() -> Result { + let mut dir = out_dir().and_then(canonicalize)?; + loop { + if dir.ends_with("target") { + return Ok(dir); + } + if !dir.pop() { + return Err(Error::TargetDir); + } + } +} + +#[cfg(not(windows))] +fn canonicalize(path: impl AsRef) -> Result { + Ok(fs::canonicalize(path)?) +} + +#[cfg(windows)] +fn canonicalize(path: impl AsRef) -> Result { + // Real fs::canonicalize on Windows produces UNC paths which cl.exe is + // unable to handle in includes. Use a poor approximation instead. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + Ok(env::current_dir()?.join(path)) +} + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_or_copy; + +#[cfg(windows)] +fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { + use std::os::windows::fs::symlink_file; + + // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they + // require Developer Mode. If it fails, fall back to copying the file. + if symlink_file(src, dst).is_err() { + fs::copy(src, dst)?; + } + Ok(()) +} + +#[cfg(not(any(unix, windows)))] +use std::fs::copy as symlink_or_copy; diff --git a/gen/build/src/syntax b/gen/build/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/build/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml new file mode 100644 index 0000000..742d54b --- /dev/null +++ b/gen/cmd/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cxxbridge-cmd" +version = "0.2.12" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." +repository = "https://github.com/dtolnay/cxx" + +[[bin]] +name = "cxxbridge" +path = "src/main.rs" + +[badges] +travis-ci = { repository = "dtolnay/cxx" } + +[dependencies] +anyhow = "1.0" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0", features = ["span-locations"] } +quote = "1.0" +structopt = "0.3" +syn = { version = "1.0", features = ["full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/LICENSE-APACHE b/gen/cmd/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/cmd/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/cmd/LICENSE-MIT b/gen/cmd/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/cmd/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/cmd/src/gen b/gen/cmd/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/cmd/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/cmd/src/lib.rs b/gen/cmd/src/lib.rs new file mode 100644 index 0000000..8b1a393 --- /dev/null +++ b/gen/cmd/src/lib.rs @@ -0,0 +1 @@ +// empty diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs new file mode 100644 index 0000000..a20179f --- /dev/null +++ b/gen/cmd/src/main.rs @@ -0,0 +1,60 @@ +#![allow( + clippy::cognitive_complexity, + clippy::inherent_to_string, + clippy::large_enum_variant, + clippy::new_without_default, + clippy::toplevel_ref_arg +)] + +mod gen; +mod syntax; + +use gen::include; +use std::io::{self, Write}; +use std::path::PathBuf; +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt( + name = "cxxbridge", + author = "David Tolnay ", + about = "https://github.com/dtolnay/cxx", + usage = "\ + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit rust/cxx.h header to stdout", + help_message = "Print help information", + version_message = "Print version information" +)] +struct Opt { + /// Input Rust source file containing #[cxx::bridge] + #[structopt(parse(from_os_str), required_unless = "header")] + input: Option, + + /// Emit header with declarations only + #[structopt(long)] + header: bool, + + /// Any additional headers to #include + #[structopt(short, long)] + include: Vec, +} + +fn write(content: impl AsRef<[u8]>) { + let _ = io::stdout().lock().write_all(content.as_ref()); +} + +fn main() { + let opt = Opt::from_args(); + + let gen = gen::Opt { + include: opt.include, + }; + + match (opt.input, opt.header) { + (Some(input), true) => write(gen::do_generate_header(&input, gen)), + (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), + (None, true) => write(include::HEADER), + (None, false) => unreachable!(), // enforced by required_unless + } +} diff --git a/gen/cmd/src/syntax b/gen/cmd/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/cmd/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/error.rs b/gen/error.rs deleted file mode 100644 index 2e8ecc4..0000000 --- a/gen/error.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::syntax; -use anyhow::anyhow; -use codespan_reporting::diagnostic::{Diagnostic, Label}; -use codespan_reporting::files::SimpleFiles; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; -use codespan_reporting::term::{self, Config}; -use std::error::Error as StdError; -use std::fmt::{self, Display}; -use std::io::{self, Write}; -use std::ops::Range; -use std::path::Path; -use std::process; - -pub(super) type Result = std::result::Result; - -#[derive(Debug)] -pub(super) enum Error { - NoBridgeMod, - OutOfLineMod, - Io(io::Error), - Syn(syn::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), - Error::Io(err) => err.fmt(f), - Error::Syn(err) => err.fmt(f), - } - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Error::Io(err) => Some(err), - Error::Syn(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: syn::Error) -> Self { - Error::Syn(err) - } -} - -pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { - match error { - Error::Syn(syn_error) => { - let writer = StandardStream::stderr(ColorChoice::Auto); - let ref mut stderr = writer.lock(); - for error in syn_error { - let _ = writeln!(stderr); - display_syn_error(stderr, path, source, error); - } - } - _ => eprintln!("cxxbridge: {:?}", anyhow!(error)), - } - process::exit(1); -} - -fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { - let span = error.span(); - let start = span.start(); - let end = span.end(); - - let mut start_offset = 0; - for _ in 1..start.line { - start_offset += source[start_offset..].find('\n').unwrap() + 1; - } - start_offset += start.column; - - let mut end_offset = start_offset; - if start.line == end.line { - end_offset -= start.column; - } else { - for _ in 0..end.line - start.line { - end_offset += source[end_offset..].find('\n').unwrap() + 1; - } - } - end_offset += end.column; - - let mut files = SimpleFiles::new(); - let file = files.add(path.to_string_lossy(), source); - - let diagnostic = diagnose(file, start_offset..end_offset, error); - - let config = Config::default(); - let _ = term::emit(stderr, &config, &files, &diagnostic); -} - -fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { - let message = error.to_string(); - let info = syntax::error::ERRORS - .iter() - .find(|e| message.contains(e.msg)); - let mut diagnostic = Diagnostic::error().with_message(&message); - let mut label = Label::primary(file, range); - if let Some(info) = info { - label.message = info.label.map_or(message, str::to_owned); - diagnostic.labels.push(label); - diagnostic.notes.extend(info.note.map(str::to_owned)); - } else { - label.message = message; - diagnostic.labels.push(label); - } - diagnostic.code = Some("cxxbridge".to_owned()); - diagnostic -} diff --git a/gen/include b/gen/include deleted file mode 120000 index f5030fe..0000000 --- a/gen/include +++ /dev/null @@ -1 +0,0 @@ -../include \ No newline at end of file diff --git a/gen/include.rs b/gen/include.rs deleted file mode 100644 index 129a8e6..0000000 --- a/gen/include.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::fmt::{self, Display}; - -pub static HEADER: &str = include_str!("include/cxx.h"); - -pub fn get(guard: &str) -> &'static str { - let ifndef = format!("#ifndef {}", guard); - let endif = format!("#endif // {}", guard); - let begin = find_line(&ifndef); - let end = find_line(&endif); - if let (Some(begin), Some(end)) = (begin, end) { - &HEADER[begin..end + endif.len()] - } else { - panic!("not found in cxx.h header: {}", guard) - } -} - -fn find_line(line: &str) -> Option { - let mut offset = 0; - loop { - offset += HEADER[offset..].find(line)?; - let rest = &HEADER[offset + line.len()..]; - if rest.starts_with('\n') || rest.starts_with('\r') { - return Some(offset); - } - offset += line.len(); - } -} - -#[derive(Default, PartialEq)] -pub struct Includes { - custom: Vec, - pub array: bool, - pub cstddef: bool, - pub cstdint: bool, - pub cstring: bool, - pub exception: bool, - pub memory: bool, - pub string: bool, - pub type_traits: bool, - pub utility: bool, - pub vector: bool, - pub base_tsd: bool, -} - -impl Includes { - pub fn new() -> Self { - Includes::default() - } - - pub fn insert(&mut self, include: String) { - self.custom.push(include); - } -} - -impl Extend for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter); - } -} - -impl Display for Includes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for include in &self.custom { - writeln!(f, "#include \"{}\"", include.escape_default())?; - } - if self.array { - writeln!(f, "#include ")?; - } - if self.cstddef { - writeln!(f, "#include ")?; - } - if self.cstdint { - writeln!(f, "#include ")?; - } - if self.cstring { - writeln!(f, "#include ")?; - } - if self.exception { - writeln!(f, "#include ")?; - } - if self.memory { - writeln!(f, "#include ")?; - } - if self.string { - writeln!(f, "#include ")?; - } - if self.type_traits { - writeln!(f, "#include ")?; - } - if self.utility { - writeln!(f, "#include ")?; - } - if self.vector { - writeln!(f, "#include ")?; - } - if self.base_tsd { - writeln!(f, "#if defined(_WIN32)")?; - writeln!(f, "#include ")?; - writeln!(f, "#endif")?; - } - if *self != Self::default() { - writeln!(f)?; - } - Ok(()) - } -} diff --git a/gen/mod.rs b/gen/mod.rs deleted file mode 100644 index 928c6ec..0000000 --- a/gen/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Functionality that is shared between the cxx::generate_bridge entry point and -// the cmd. - -mod error; -pub(super) mod include; -pub(super) mod out; -mod write; - -use self::error::{format_err, Error, Result}; -use crate::syntax::namespace::Namespace; -use crate::syntax::{self, check, Types}; -use quote::quote; -use std::fs; -use std::path::Path; -use syn::{Attribute, File, Item}; - -struct Input { - namespace: Namespace, - module: Vec, -} - -#[derive(Default)] -pub(super) struct Opt { - /// Any additional headers to #include - pub include: Vec, -} - -pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { - let header = false; - generate(path, opt, header) -} - -pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { - let header = true; - generate(path, opt, header) -} - -fn generate(path: &Path, opt: Opt, header: bool) -> Vec { - let source = match fs::read_to_string(path) { - Ok(source) => source, - Err(err) => format_err(path, "", Error::Io(err)), - }; - match (|| -> Result<_> { - let syntax = syn::parse_file(&source)?; - let bridge = find_bridge_mod(syntax)?; - let apis = syntax::parse_items(bridge.module)?; - let types = Types::collect(&apis)?; - check::typecheck(&apis, &types)?; - let out = write::gen(bridge.namespace, &apis, &types, opt, header); - Ok(out) - })() { - Ok(out) => out.content(), - Err(err) => format_err(path, &source, err), - } -} - -fn find_bridge_mod(syntax: File) -> Result { - for item in syntax.items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path; - if quote!(#path).to_string() == "cxx :: bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Input { namespace, module }); - } - } - } - } - Err(Error::NoBridgeMod) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} diff --git a/gen/out.rs b/gen/out.rs deleted file mode 100644 index 08bf85f..0000000 --- a/gen/out.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; -use std::cell::RefCell; -use std::fmt::{self, Arguments, Write}; - -pub(crate) struct OutFile { - pub namespace: Namespace, - pub header: bool, - pub include: Includes, - content: RefCell, -} - -struct Content { - bytes: Vec, - section_pending: bool, - blocks_pending: Vec<&'static str>, -} - -impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { - OutFile { - namespace, - header, - include: Includes::new(), - content: RefCell::new(Content { - bytes: Vec::new(), - section_pending: false, - blocks_pending: Vec::new(), - }), - } - } - - // Write a blank line if the preceding section had any contents. - pub fn next_section(&mut self) { - let content = self.content.get_mut(); - content.section_pending = true; - } - - pub fn begin_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - content.blocks_pending.push(block); - } - - pub fn end_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - if content.blocks_pending.pop().is_none() { - content.bytes.extend_from_slice(b"} // "); - content.bytes.extend_from_slice(block.as_bytes()); - content.bytes.push(b'\n'); - content.section_pending = true; - } - } - - pub fn prepend(&mut self, section: String) { - let content = self.content.get_mut(); - content.bytes.splice(..0, section.into_bytes()); - } - - pub fn write_fmt(&self, args: Arguments) { - let content = &mut *self.content.borrow_mut(); - Write::write_fmt(content, args).unwrap(); - } - - pub fn content(&self) -> Vec { - self.content.borrow().bytes.clone() - } -} - -impl Write for Content { - fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { - if !self.blocks_pending.is_empty() { - if !self.bytes.is_empty() { - self.bytes.push(b'\n'); - } - for block in self.blocks_pending.drain(..) { - self.bytes.extend_from_slice(block.as_bytes()); - self.bytes.extend_from_slice(b" {\n"); - } - self.section_pending = false; - } else if self.section_pending { - if !self.bytes.is_empty() { - self.bytes.push(b'\n'); - } - self.section_pending = false; - } - self.bytes.extend_from_slice(s.as_bytes()); - } - Ok(()) - } -} diff --git a/gen/src/error.rs b/gen/src/error.rs new file mode 100644 index 0000000..2e8ecc4 --- /dev/null +++ b/gen/src/error.rs @@ -0,0 +1,119 @@ +use crate::syntax; +use anyhow::anyhow; +use codespan_reporting::diagnostic::{Diagnostic, Label}; +use codespan_reporting::files::SimpleFiles; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; +use codespan_reporting::term::{self, Config}; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io::{self, Write}; +use std::ops::Range; +use std::path::Path; +use std::process; + +pub(super) type Result = std::result::Result; + +#[derive(Debug)] +pub(super) enum Error { + NoBridgeMod, + OutOfLineMod, + Io(io::Error), + Syn(syn::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), + Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), + Error::Io(err) => err.fmt(f), + Error::Syn(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + Error::Syn(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: syn::Error) -> Self { + Error::Syn(err) + } +} + +pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { + match error { + Error::Syn(syn_error) => { + let writer = StandardStream::stderr(ColorChoice::Auto); + let ref mut stderr = writer.lock(); + for error in syn_error { + let _ = writeln!(stderr); + display_syn_error(stderr, path, source, error); + } + } + _ => eprintln!("cxxbridge: {:?}", anyhow!(error)), + } + process::exit(1); +} + +fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { + let span = error.span(); + let start = span.start(); + let end = span.end(); + + let mut start_offset = 0; + for _ in 1..start.line { + start_offset += source[start_offset..].find('\n').unwrap() + 1; + } + start_offset += start.column; + + let mut end_offset = start_offset; + if start.line == end.line { + end_offset -= start.column; + } else { + for _ in 0..end.line - start.line { + end_offset += source[end_offset..].find('\n').unwrap() + 1; + } + } + end_offset += end.column; + + let mut files = SimpleFiles::new(); + let file = files.add(path.to_string_lossy(), source); + + let diagnostic = diagnose(file, start_offset..end_offset, error); + + let config = Config::default(); + let _ = term::emit(stderr, &config, &files, &diagnostic); +} + +fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { + let message = error.to_string(); + let info = syntax::error::ERRORS + .iter() + .find(|e| message.contains(e.msg)); + let mut diagnostic = Diagnostic::error().with_message(&message); + let mut label = Label::primary(file, range); + if let Some(info) = info { + label.message = info.label.map_or(message, str::to_owned); + diagnostic.labels.push(label); + diagnostic.notes.extend(info.note.map(str::to_owned)); + } else { + label.message = message; + diagnostic.labels.push(label); + } + diagnostic.code = Some("cxxbridge".to_owned()); + diagnostic +} diff --git a/gen/src/include b/gen/src/include new file mode 120000 index 0000000..fcffffb --- /dev/null +++ b/gen/src/include @@ -0,0 +1 @@ +../../include \ No newline at end of file diff --git a/gen/src/include.rs b/gen/src/include.rs new file mode 100644 index 0000000..129a8e6 --- /dev/null +++ b/gen/src/include.rs @@ -0,0 +1,106 @@ +use std::fmt::{self, Display}; + +pub static HEADER: &str = include_str!("include/cxx.h"); + +pub fn get(guard: &str) -> &'static str { + let ifndef = format!("#ifndef {}", guard); + let endif = format!("#endif // {}", guard); + let begin = find_line(&ifndef); + let end = find_line(&endif); + if let (Some(begin), Some(end)) = (begin, end) { + &HEADER[begin..end + endif.len()] + } else { + panic!("not found in cxx.h header: {}", guard) + } +} + +fn find_line(line: &str) -> Option { + let mut offset = 0; + loop { + offset += HEADER[offset..].find(line)?; + let rest = &HEADER[offset + line.len()..]; + if rest.starts_with('\n') || rest.starts_with('\r') { + return Some(offset); + } + offset += line.len(); + } +} + +#[derive(Default, PartialEq)] +pub struct Includes { + custom: Vec, + pub array: bool, + pub cstddef: bool, + pub cstdint: bool, + pub cstring: bool, + pub exception: bool, + pub memory: bool, + pub string: bool, + pub type_traits: bool, + pub utility: bool, + pub vector: bool, + pub base_tsd: bool, +} + +impl Includes { + pub fn new() -> Self { + Includes::default() + } + + pub fn insert(&mut self, include: String) { + self.custom.push(include); + } +} + +impl Extend for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter); + } +} + +impl Display for Includes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for include in &self.custom { + writeln!(f, "#include \"{}\"", include.escape_default())?; + } + if self.array { + writeln!(f, "#include ")?; + } + if self.cstddef { + writeln!(f, "#include ")?; + } + if self.cstdint { + writeln!(f, "#include ")?; + } + if self.cstring { + writeln!(f, "#include ")?; + } + if self.exception { + writeln!(f, "#include ")?; + } + if self.memory { + writeln!(f, "#include ")?; + } + if self.string { + writeln!(f, "#include ")?; + } + if self.type_traits { + writeln!(f, "#include ")?; + } + if self.utility { + writeln!(f, "#include ")?; + } + if self.vector { + writeln!(f, "#include ")?; + } + if self.base_tsd { + writeln!(f, "#if defined(_WIN32)")?; + writeln!(f, "#include ")?; + writeln!(f, "#endif")?; + } + if *self != Self::default() { + writeln!(f)?; + } + Ok(()) + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs new file mode 100644 index 0000000..928c6ec --- /dev/null +++ b/gen/src/mod.rs @@ -0,0 +1,87 @@ +// Functionality that is shared between the cxx::generate_bridge entry point and +// the cmd. + +mod error; +pub(super) mod include; +pub(super) mod out; +mod write; + +use self::error::{format_err, Error, Result}; +use crate::syntax::namespace::Namespace; +use crate::syntax::{self, check, Types}; +use quote::quote; +use std::fs; +use std::path::Path; +use syn::{Attribute, File, Item}; + +struct Input { + namespace: Namespace, + module: Vec, +} + +#[derive(Default)] +pub(super) struct Opt { + /// Any additional headers to #include + pub include: Vec, +} + +pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { + let header = false; + generate(path, opt, header) +} + +pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { + let header = true; + generate(path, opt, header) +} + +fn generate(path: &Path, opt: Opt, header: bool) -> Vec { + let source = match fs::read_to_string(path) { + Ok(source) => source, + Err(err) => format_err(path, "", Error::Io(err)), + }; + match (|| -> Result<_> { + let syntax = syn::parse_file(&source)?; + let bridge = find_bridge_mod(syntax)?; + let apis = syntax::parse_items(bridge.module)?; + let types = Types::collect(&apis)?; + check::typecheck(&apis, &types)?; + let out = write::gen(bridge.namespace, &apis, &types, opt, header); + Ok(out) + })() { + Ok(out) => out.content(), + Err(err) => format_err(path, &source, err), + } +} + +fn find_bridge_mod(syntax: File) -> Result { + for item in syntax.items { + if let Item::Mod(item) = item { + for attr in &item.attrs { + let path = &attr.path; + if quote!(#path).to_string() == "cxx :: bridge" { + let module = match item.content { + Some(module) => module.1, + None => { + return Err(Error::Syn(syn::Error::new_spanned( + item, + Error::OutOfLineMod, + ))); + } + }; + let namespace = parse_args(attr)?; + return Ok(Input { namespace, module }); + } + } + } + } + Err(Error::NoBridgeMod) +} + +fn parse_args(attr: &Attribute) -> syn::Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() + } +} diff --git a/gen/src/out.rs b/gen/src/out.rs new file mode 100644 index 0000000..08bf85f --- /dev/null +++ b/gen/src/out.rs @@ -0,0 +1,91 @@ +use crate::gen::include::Includes; +use crate::syntax::namespace::Namespace; +use std::cell::RefCell; +use std::fmt::{self, Arguments, Write}; + +pub(crate) struct OutFile { + pub namespace: Namespace, + pub header: bool, + pub include: Includes, + content: RefCell, +} + +struct Content { + bytes: Vec, + section_pending: bool, + blocks_pending: Vec<&'static str>, +} + +impl OutFile { + pub fn new(namespace: Namespace, header: bool) -> Self { + OutFile { + namespace, + header, + include: Includes::new(), + content: RefCell::new(Content { + bytes: Vec::new(), + section_pending: false, + blocks_pending: Vec::new(), + }), + } + } + + // Write a blank line if the preceding section had any contents. + pub fn next_section(&mut self) { + let content = self.content.get_mut(); + content.section_pending = true; + } + + pub fn begin_block(&mut self, block: &'static str) { + let content = self.content.get_mut(); + content.blocks_pending.push(block); + } + + pub fn end_block(&mut self, block: &'static str) { + let content = self.content.get_mut(); + if content.blocks_pending.pop().is_none() { + content.bytes.extend_from_slice(b"} // "); + content.bytes.extend_from_slice(block.as_bytes()); + content.bytes.push(b'\n'); + content.section_pending = true; + } + } + + pub fn prepend(&mut self, section: String) { + let content = self.content.get_mut(); + content.bytes.splice(..0, section.into_bytes()); + } + + pub fn write_fmt(&self, args: Arguments) { + let content = &mut *self.content.borrow_mut(); + Write::write_fmt(content, args).unwrap(); + } + + pub fn content(&self) -> Vec { + self.content.borrow().bytes.clone() + } +} + +impl Write for Content { + fn write_str(&mut self, s: &str) -> fmt::Result { + if !s.is_empty() { + if !self.blocks_pending.is_empty() { + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); + } + for block in self.blocks_pending.drain(..) { + self.bytes.extend_from_slice(block.as_bytes()); + self.bytes.extend_from_slice(b" {\n"); + } + self.section_pending = false; + } else if self.section_pending { + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); + } + self.section_pending = false; + } + self.bytes.extend_from_slice(s.as_bytes()); + } + Ok(()) + } +} diff --git a/gen/src/write.rs b/gen/src/write.rs new file mode 100644 index 0000000..b39d978 --- /dev/null +++ b/gen/src/write.rs @@ -0,0 +1,1224 @@ +use crate::gen::out::OutFile; +use crate::gen::{include, Opt}; +use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; +use crate::syntax::symbol::Symbol; +use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use proc_macro2::Ident; +use std::collections::HashMap; + +pub(super) fn gen( + namespace: Namespace, + apis: &[Api], + types: &Types, + opt: Opt, + header: bool, +) -> OutFile { + let mut out_file = OutFile::new(namespace.clone(), header); + let out = &mut out_file; + + if header { + writeln!(out, "#pragma once"); + } + + out.include.extend(opt.include); + for api in apis { + if let Api::Include(include) = api { + out.include.insert(include.value()); + } + } + + write_includes(out, types); + write_include_cxxbridge(out, apis, types); + + out.next_section(); + for name in &namespace { + writeln!(out, "namespace {} {{", name); + } + + out.next_section(); + for api in apis { + match api { + Api::Struct(strct) => write_struct_decl(out, &strct.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident), + _ => {} + } + } + + let mut methods_for_type = HashMap::new(); + for api in apis { + if let Api::RustFunction(efn) = api { + if let Some(receiver) = &efn.sig.receiver { + methods_for_type + .entry(&receiver.ty) + .or_insert_with(Vec::new) + .push(efn); + } + } + } + + for api in apis { + match api { + Api::Struct(strct) => { + out.next_section(); + write_struct(out, strct); + } + Api::RustType(ety) => { + if let Some(methods) = methods_for_type.get(&ety.ident) { + out.next_section(); + write_struct_with_methods(out, ety, methods); + } + } + _ => {} + } + } + + if !header { + out.begin_block("extern \"C\""); + write_exception_glue(out, apis); + for api in apis { + let (efn, write): (_, fn(_, _, _)) = match api { + Api::CxxFunction(efn) => (efn, write_cxx_function_shim), + Api::RustFunction(efn) => (efn, write_rust_function_decl), + _ => continue, + }; + out.next_section(); + write(out, efn, types); + } + out.end_block("extern \"C\""); + } + + for api in apis { + if let Api::RustFunction(efn) = api { + out.next_section(); + write_rust_function_shim(out, efn, types); + } + } + + out.next_section(); + for name in namespace.iter().rev() { + writeln!(out, "}} // namespace {}", name); + } + + if !header { + out.next_section(); + write_generic_instantiations(out, types); + } + + out.prepend(out.include.to_string()); + + out_file +} + +fn write_includes(out: &mut OutFile, types: &Types) { + for ty in types { + match ty { + Type::Ident(ident) => match Atom::from(ident) { + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) + | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, + Some(CxxString) => out.include.string = true, + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} + }, + Type::RustBox(_) => out.include.type_traits = true, + Type::UniquePtr(_) => out.include.memory = true, + Type::CxxVector(_) => out.include.vector = true, + Type::SliceRefU8(_) => out.include.cstdint = true, + _ => {} + } + } +} + +fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { + let mut needs_rust_string = false; + let mut needs_rust_str = false; + let mut needs_rust_slice = false; + let mut needs_rust_box = false; + let mut needs_rust_vec = false; + let mut needs_rust_fn = false; + let mut needs_rust_isize = false; + for ty in types { + match ty { + Type::RustBox(_) => { + out.include.type_traits = true; + needs_rust_box = true; + } + Type::RustVec(_) => { + out.include.array = true; + out.include.type_traits = true; + needs_rust_vec = true; + } + Type::Str(_) => { + out.include.cstdint = true; + out.include.string = true; + needs_rust_str = true; + } + Type::Fn(_) => { + needs_rust_fn = true; + } + Type::Slice(_) | Type::SliceRefU8(_) => { + needs_rust_slice = true; + } + ty if ty == Isize => { + out.include.base_tsd = true; + needs_rust_isize = true; + } + ty if ty == RustString => { + out.include.array = true; + out.include.cstdint = true; + out.include.string = true; + needs_rust_string = true; + } + _ => {} + } + } + + let mut needs_rust_error = false; + let mut needs_unsafe_bitcopy = false; + let mut needs_manually_drop = false; + let mut needs_maybe_uninit = false; + let mut needs_trycatch = false; + for api in apis { + match api { + Api::CxxFunction(efn) if !out.header => { + if efn.throws { + needs_trycatch = true; + } + for arg in &efn.args { + let bitcopy = match arg.ty { + Type::RustVec(_) => true, + _ => arg.ty == RustString, + }; + if bitcopy { + needs_unsafe_bitcopy = true; + break; + } + } + } + Api::RustFunction(efn) if !out.header => { + if efn.throws { + out.include.exception = true; + needs_rust_error = true; + } + for arg in &efn.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + needs_manually_drop = true; + break; + } + } + if let Some(ret) = &efn.ret { + if types.needs_indirect_abi(ret) { + needs_maybe_uninit = true; + } + } + } + _ => {} + } + } + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge02"); + + if needs_rust_string + || needs_rust_str + || needs_rust_slice + || needs_rust_box + || needs_rust_vec + || needs_rust_fn + || needs_rust_error + || needs_rust_isize + || needs_unsafe_bitcopy + || needs_manually_drop + || needs_maybe_uninit + || needs_trycatch + { + writeln!(out, "// #include \"rust/cxx.h\""); + } + + if needs_rust_string { + out.next_section(); + writeln!(out, "struct unsafe_bitcopy_t;"); + } + + write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); + write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); + write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); + write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); + write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); + write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); + write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); + + if needs_manually_drop { + out.next_section(); + out.include.utility = true; + writeln!(out, "template "); + writeln!(out, "union ManuallyDrop {{"); + writeln!(out, " T value;"); + writeln!( + out, + " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", + ); + writeln!(out, " ~ManuallyDrop() {{}}"); + writeln!(out, "}};"); + } + + if needs_maybe_uninit { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union MaybeUninit {{"); + writeln!(out, " T value;"); + writeln!(out, " MaybeUninit() {{}}"); + writeln!(out, " ~MaybeUninit() {{}}"); + writeln!(out, "}};"); + } + + out.end_block("namespace cxxbridge02"); + + if needs_trycatch { + out.begin_block("namespace behavior"); + out.include.exception = true; + out.include.type_traits = true; + out.include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); + writeln!(out); + writeln!(out, "template "); + writeln!(out, "static typename std::enable_if<"); + writeln!( + out, + " std::is_same(), std::declval())),", + ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + out.end_block("namespace behavior"); + } + + out.end_block("namespace rust"); +} + +fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { + let section = include::get(section); + if needed { + out.next_section(); + for line in section.lines() { + if !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + } +} + +fn write_struct(out: &mut OutFile, strct: &Struct) { + for line in strct.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", strct.ident); + for field in &strct.fields { + write!(out, " "); + write_type_space(out, &field.ty); + writeln!(out, "{};", field.ident); + } + writeln!(out, "}};"); +} + +fn write_struct_decl(out: &mut OutFile, ident: &Ident) { + writeln!(out, "struct {};", ident); +} + +fn write_struct_using(out: &mut OutFile, ident: &Ident) { + writeln!(out, "using {} = {};", ident, ident); +} + +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { + for line in ety.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", ety.ident); + writeln!(out, " {}() = delete;", ety.ident); + writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + for method in methods { + write!(out, " "); + let sig = &method.sig; + let local_name = method.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, false); + writeln!(out, ";"); + } + writeln!(out, "}};"); +} + +fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { + let mut has_cxx_throws = false; + for api in apis { + if let Api::CxxFunction(efn) = api { + if efn.throws { + has_cxx_throws = true; + break; + } + } + } + + if has_cxx_throws { + out.next_section(); + writeln!( + out, + "const char *cxxbridge02$exception(const char *, size_t);", + ); + } +} + +fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type_space(out, &efn.ret, types); + } + let mangled = mangle::extern_fn(&out.namespace, efn); + write!(out, "{}(", mangled); + if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } + write!(out, "{} &self", receiver.ty); + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 || efn.receiver.is_some() { + write!(out, ", "); + } + if arg.ty == RustString { + write!(out, "const "); + } else if let Type::RustVec(_) = arg.ty { + write!(out, "const "); + } + write_extern_arg(out, arg, types); + } + let indirect_return = indirect_return(efn, types); + if indirect_return { + if !efn.args.is_empty() { + write!(out, ", "); + } + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); + write!(out, "*return$"); + } + writeln!(out, ") noexcept {{"); + write!(out, " "); + write_return_type(out, &efn.ret); + match &efn.receiver { + None => write!(out, "(*{}$)(", efn.ident), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")"); + if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } + } + write!(out, " = "); + match &efn.receiver { + None => write!(out, "{}", efn.ident), + Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), + } + writeln!(out, ";"); + write!(out, " "); + if efn.throws { + writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, " ::rust::behavior::trycatch("); + writeln!(out, " [&] {{"); + write!(out, " "); + } + if indirect_return { + write!(out, "new (return$) "); + write_indirect_return_type(out, efn.ret.as_ref().unwrap()); + write!(out, "("); + } else if efn.ret.is_some() { + write!(out, "return "); + } + match &efn.ret { + Some(Type::Ref(_)) => write!(out, "&"), + Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => { + write!(out, "::rust::Slice::Repr(") + } + _ => {} + } + match &efn.receiver { + None => write!(out, "{}$(", efn.ident), + Some(_) => write!(out, "(self.*{}$)(", efn.ident), + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + if let Type::RustBox(_) = &arg.ty { + write_type(out, &arg.ty); + write!(out, "::from_raw({})", arg.ident); + } else if let Type::UniquePtr(_) = &arg.ty { + write_type(out, &arg.ty); + write!(out, "({})", arg.ident); + } else if arg.ty == RustString { + write!( + out, + "::rust::String(::rust::unsafe_bitcopy, *{})", + arg.ident, + ); + } else if let Type::RustVec(_) = arg.ty { + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); + } else if types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; + write!(out, "::std::move(*{})", arg.ident); + } else { + write!(out, "{}", arg.ident); + } + } + write!(out, ")"); + match &efn.ret { + Some(Type::RustBox(_)) => write!(out, ".into_raw()"), + Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::CxxVector(_)) => write!( + out, + " /* Use RVO to convert to r-value and move construct */" + ), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), + _ => {} + } + if indirect_return { + write!(out, ")"); + } + writeln!(out, ";"); + if efn.throws { + out.include.cstring = true; + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }},"); + writeln!(out, " [&](const char *catch$) noexcept {{"); + writeln!(out, " throw$.len = ::std::strlen(catch$);"); + writeln!( + out, + " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", + ); + writeln!(out, " }});"); + writeln!(out, " return throw$;"); + } + writeln!(out, "}}"); + for arg in &efn.args { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + write_function_pointer_trampoline(out, efn, var, f, types); + } + } +} + +fn write_function_pointer_trampoline( + out: &mut OutFile, + efn: &ExternFn, + var: &Ident, + f: &Signature, + types: &Types, +) { + out.next_section(); + let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let indirect_call = true; + write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); + + out.next_section(); + let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); +} + +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { + let link_name = mangle::extern_fn(&out.namespace, efn); + let indirect_call = false; + write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); +} + +fn write_rust_function_decl_impl( + out: &mut OutFile, + link_name: &Symbol, + sig: &Signature, + types: &Types, + indirect_call: bool, +) { + if sig.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type_space(out, &sig.ret, types); + } + write!(out, "{}(", link_name); + let mut needs_comma = false; + if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } + write!(out, "{} &self", receiver.ty); + needs_comma = true; + } + for arg in &sig.args { + if needs_comma { + write!(out, ", "); + } + write_extern_arg(out, arg, types); + needs_comma = true; + } + if indirect_return(sig, types) { + if needs_comma { + write!(out, ", "); + } + write_return_type(out, &sig.ret); + write!(out, "*return$"); + needs_comma = true; + } + if indirect_call { + if needs_comma { + write!(out, ", "); + } + write!(out, "void *"); + } + writeln!(out, ") noexcept;"); +} + +fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { + for line in efn.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + let local_name = match &efn.sig.receiver { + None => efn.ident.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), + }; + let invoke = mangle::extern_fn(&out.namespace, efn); + let indirect_call = false; + write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); +} + +fn write_rust_function_shim_decl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + indirect_call: bool, +) { + write_return_type(out, &sig.ret); + write!(out, "{}(", local_name); + for (i, arg) in sig.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type_space(out, &arg.ty); + write!(out, "{}", arg.ident); + } + if indirect_call { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "void *extern$"); + } + write!(out, ")"); + if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } + } + if !sig.throws { + write!(out, " noexcept"); + } +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &Symbol, + indirect_call: bool, +) { + if out.header && sig.receiver.is_some() { + // We've already defined this inside the struct. + return; + } + write_rust_function_shim_decl(out, local_name, sig, indirect_call); + if out.header { + writeln!(out, ";"); + return; + } + writeln!(out, " {{"); + for arg in &sig.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; + write!(out, " ::rust::ManuallyDrop<"); + write_type(out, &arg.ty); + writeln!(out, "> {}$(::std::move({0}));", arg.ident); + } + } + write!(out, " "); + let indirect_return = indirect_return(sig, types); + if indirect_return { + write!(out, "::rust::MaybeUninit<"); + write_type(out, sig.ret.as_ref().unwrap()); + writeln!(out, "> return$;"); + write!(out, " "); + } else if let Some(ret) = &sig.ret { + write!(out, "return "); + match ret { + Type::RustBox(_) => { + write_type(out, ret); + write!(out, "::from_raw("); + } + Type::UniquePtr(_) => { + write_type(out, ret); + write!(out, "("); + } + Type::Ref(_) => write!(out, "*"), + _ => {} + } + } + if sig.throws { + write!(out, "::rust::Str::Repr error$ = "); + } + write!(out, "{}(", invoke); + if sig.receiver.is_some() { + write!(out, "*this"); + } + for (i, arg) in sig.args.iter().enumerate() { + if i > 0 || sig.receiver.is_some() { + write!(out, ", "); + } + match &arg.ty { + Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), + ty if types.needs_indirect_abi(ty) => write!(out, "&"), + _ => {} + } + write!(out, "{}", arg.ident); + match &arg.ty { + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::UniquePtr(_) => write!(out, ".release()"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), + ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), + _ => {} + } + } + if indirect_return { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "&return$.value"); + } + if indirect_call { + if !sig.args.is_empty() || indirect_return { + write!(out, ", "); + } + write!(out, "extern$"); + } + write!(out, ")"); + if let Some(ret) = &sig.ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + write!(out, ")"); + } + } + writeln!(out, ";"); + if sig.throws { + writeln!(out, " if (error$.ptr) {{"); + writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " }}"); + } + if indirect_return { + out.include.utility = true; + writeln!(out, " return ::std::move(return$.value);"); + } + writeln!(out, "}}"); +} + +fn write_return_type(out: &mut OutFile, ty: &Option) { + match ty { + None => write!(out, "void "), + Some(ty) => write_type_space(out, ty), + } +} + +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret + .as_ref() + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) +} + +fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::RustBox(ty) | Type::UniquePtr(ty) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Type::Ref(ty) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } + Type::Str(_) => write!(out, "::rust::Str::Repr"), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), + _ => write_type(out, ty), + } +} + +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { + write_indirect_return_type(out, ty); + match ty { + Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} + Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), + _ => write_space_after_type(out, ty), + } +} + +fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { + match ty { + Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Some(Type::Ref(ty)) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } + Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), + Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), + Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), + _ => write_return_type(out, ty), + } +} + +fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { + match &arg.ty { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Type::Str(_) => write!(out, "::rust::Str::Repr "), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), + _ => write_type_space(out, &arg.ty), + } + if types.needs_indirect_abi(&arg.ty) { + write!(out, "*"); + } + write!(out, "{}", arg.ident); +} + +fn write_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::Ident(ident) => match Atom::from(ident) { + Some(Bool) => write!(out, "bool"), + Some(U8) => write!(out, "uint8_t"), + Some(U16) => write!(out, "uint16_t"), + Some(U32) => write!(out, "uint32_t"), + Some(U64) => write!(out, "uint64_t"), + Some(Usize) => write!(out, "size_t"), + Some(I8) => write!(out, "int8_t"), + Some(I16) => write!(out, "int16_t"), + Some(I32) => write!(out, "int32_t"), + Some(I64) => write!(out, "int64_t"), + Some(Isize) => write!(out, "::rust::isize"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), + Some(CxxString) => write!(out, "::std::string"), + Some(RustString) => write!(out, "::rust::String"), + None => write!(out, "{}", ident), + }, + Type::RustBox(ty) => { + write!(out, "::rust::Box<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::RustVec(ty) => { + write!(out, "::rust::Vec<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::UniquePtr(ptr) => { + write!(out, "::std::unique_ptr<"); + write_type(out, &ptr.inner); + write!(out, ">"); + } + Type::CxxVector(ty) => { + write!(out, "::std::vector<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::Ref(r) => { + if r.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &r.inner); + write!(out, " &"); + } + Type::Slice(_) => { + // For now, only U8 slices are supported, which are covered separately below + unreachable!() + } + Type::Str(_) => { + write!(out, "::rust::Str"); + } + Type::SliceRefU8(_) => { + write!(out, "::rust::Slice"); + } + Type::Fn(f) => { + write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); + match &f.ret { + Some(ret) => write_type(out, ret), + None => write!(out, "void"), + } + write!(out, "("); + for (i, arg) in f.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")>"); + } + Type::Void(_) => unreachable!(), + } +} + +fn write_type_space(out: &mut OutFile, ty: &Type) { + write_type(out, ty); + write_space_after_type(out, ty); +} + +fn write_space_after_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::Ident(_) + | Type::RustBox(_) + | Type::UniquePtr(_) + | Type::Str(_) + | Type::CxxVector(_) + | Type::RustVec(_) + | Type::SliceRefU8(_) + | Type::Fn(_) => write!(out, " "), + Type::Ref(_) => {} + Type::Void(_) | Type::Slice(_) => unreachable!(), + } +} + +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_typename(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut path = String::new(); + for name in namespace { + path += name; + path += "::"; + } + path += &ident.to_string(); + path + } + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + _ => unreachable!(), + } +} + +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_mangled(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + _ => unreachable!(), + } +} + +fn write_generic_instantiations(out: &mut OutFile, types: &Types) { + fn allow_unique_ptr(ident: &Ident) -> bool { + Atom::from(ident).is_none() + } + + out.begin_block("extern \"C\""); + for ty in types { + if let Type::RustBox(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + out.next_section(); + write_rust_box_extern(out, inner); + } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_rust_vec_extern(out, inner); + } + } + } else if let Type::UniquePtr(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if allow_unique_ptr(inner) { + out.next_section(); + write_unique_ptr(out, inner, types); + } + } + } else if let Type::CxxVector(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_cxx_vector(out, ty, inner, types); + } + } + } + } + out.end_block("extern \"C\""); + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge02"); + for ty in types { + if let Type::RustBox(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + write_rust_box_impl(out, inner); + } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + write_rust_vec_impl(out, inner); + } + } + } + } + out.end_block("namespace cxxbridge02"); + out.end_block("namespace rust"); +} + +fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + for name in &out.namespace { + inner += name; + inner += "::"; + } + inner += &ident.to_string(); + let instance = inner.replace("::", "$"); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!( + out, + "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); +} + +fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!( + out, + "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + inner, instance, + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$stride() noexcept;", + instance, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); +} + +fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + for name in &out.namespace { + inner += name; + inner += "::"; + } + inner += &ident.to_string(); + let instance = inner.replace("::", "$"); + + writeln!(out, "template <>"); + writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); + writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "void Box<{}>::drop() noexcept {{", inner); + writeln!(out, " cxxbridge02$box${}$drop(this);", instance); + writeln!(out, "}}"); +} + +fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "template <>"); + writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); + writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$drop(this);", + instance, + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$data(this);", + instance, + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); + writeln!(out, "}}"); +} + +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { + let ty = Type::Ident(ident.clone()); + let instance = to_mangled(&out.namespace, &ty); + + writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + + write_unique_ptr_common(out, &ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); +} + +// Shared by UniquePtr and UniquePtr>. +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.utility = true; + let inner = to_typename(&out.namespace, ty); + let instance = to_mangled(&out.namespace, ty); + + let can_construct_from_value = match ty { + Type::Ident(ident) => types.structs.contains_key(ident), + _ => false, + }; + + writeln!( + out, + "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", + inner, + ); + writeln!( + out, + "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", + inner, + ); + writeln!( + out, + "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + instance, inner, + ); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); + writeln!(out, "}}"); + if can_construct_from_value { + writeln!( + out, + "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + instance, inner, inner, + ); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } + writeln!( + out, + "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + instance, inner, inner, + ); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); + writeln!(out, "}}"); + writeln!( + out, + "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return ptr.get();"); + writeln!(out, "}}"); + writeln!( + out, + "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return ptr.release();"); + writeln!(out, "}}"); + writeln!( + out, + "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + instance, inner, + ); + writeln!(out, " ptr->~unique_ptr();"); + writeln!(out, "}}"); +} + +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); + writeln!( + out, + "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", + instance, inner, + ); + writeln!(out, " return s.size();"); + writeln!(out, "}}"); + writeln!( + out, + "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return s[pos];"); + writeln!(out, "}}"); + + write_unique_ptr_common(out, vector_ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); +} diff --git a/gen/write.rs b/gen/write.rs deleted file mode 100644 index b39d978..0000000 --- a/gen/write.rs +++ /dev/null @@ -1,1224 +0,0 @@ -use crate::gen::out::OutFile; -use crate::gen::{include, Opt}; -use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; -use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; -use proc_macro2::Ident; -use std::collections::HashMap; - -pub(super) fn gen( - namespace: Namespace, - apis: &[Api], - types: &Types, - opt: Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); - let out = &mut out_file; - - if header { - writeln!(out, "#pragma once"); - } - - out.include.extend(opt.include); - for api in apis { - if let Api::Include(include) = api { - out.include.insert(include.value()); - } - } - - write_includes(out, types); - write_include_cxxbridge(out, apis, types); - - out.next_section(); - for name in &namespace { - writeln!(out, "namespace {} {{", name); - } - - out.next_section(); - for api in apis { - match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), - _ => {} - } - } - - let mut methods_for_type = HashMap::new(); - for api in apis { - if let Api::RustFunction(efn) = api { - if let Some(receiver) = &efn.sig.receiver { - methods_for_type - .entry(&receiver.ty) - .or_insert_with(Vec::new) - .push(efn); - } - } - } - - for api in apis { - match api { - Api::Struct(strct) => { - out.next_section(); - write_struct(out, strct); - } - Api::RustType(ety) => { - if let Some(methods) = methods_for_type.get(&ety.ident) { - out.next_section(); - write_struct_with_methods(out, ety, methods); - } - } - _ => {} - } - } - - if !header { - out.begin_block("extern \"C\""); - write_exception_glue(out, apis); - for api in apis { - let (efn, write): (_, fn(_, _, _)) = match api { - Api::CxxFunction(efn) => (efn, write_cxx_function_shim), - Api::RustFunction(efn) => (efn, write_rust_function_decl), - _ => continue, - }; - out.next_section(); - write(out, efn, types); - } - out.end_block("extern \"C\""); - } - - for api in apis { - if let Api::RustFunction(efn) = api { - out.next_section(); - write_rust_function_shim(out, efn, types); - } - } - - out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - - if !header { - out.next_section(); - write_generic_instantiations(out, types); - } - - out.prepend(out.include.to_string()); - - out_file -} - -fn write_includes(out: &mut OutFile, types: &Types) { - for ty in types { - match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) - | Some(I64) => out.include.cstdint = true, - Some(Usize) => out.include.cstddef = true, - Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} - }, - Type::RustBox(_) => out.include.type_traits = true, - Type::UniquePtr(_) => out.include.memory = true, - Type::CxxVector(_) => out.include.vector = true, - Type::SliceRefU8(_) => out.include.cstdint = true, - _ => {} - } - } -} - -fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { - let mut needs_rust_string = false; - let mut needs_rust_str = false; - let mut needs_rust_slice = false; - let mut needs_rust_box = false; - let mut needs_rust_vec = false; - let mut needs_rust_fn = false; - let mut needs_rust_isize = false; - for ty in types { - match ty { - Type::RustBox(_) => { - out.include.type_traits = true; - needs_rust_box = true; - } - Type::RustVec(_) => { - out.include.array = true; - out.include.type_traits = true; - needs_rust_vec = true; - } - Type::Str(_) => { - out.include.cstdint = true; - out.include.string = true; - needs_rust_str = true; - } - Type::Fn(_) => { - needs_rust_fn = true; - } - Type::Slice(_) | Type::SliceRefU8(_) => { - needs_rust_slice = true; - } - ty if ty == Isize => { - out.include.base_tsd = true; - needs_rust_isize = true; - } - ty if ty == RustString => { - out.include.array = true; - out.include.cstdint = true; - out.include.string = true; - needs_rust_string = true; - } - _ => {} - } - } - - let mut needs_rust_error = false; - let mut needs_unsafe_bitcopy = false; - let mut needs_manually_drop = false; - let mut needs_maybe_uninit = false; - let mut needs_trycatch = false; - for api in apis { - match api { - Api::CxxFunction(efn) if !out.header => { - if efn.throws { - needs_trycatch = true; - } - for arg in &efn.args { - let bitcopy = match arg.ty { - Type::RustVec(_) => true, - _ => arg.ty == RustString, - }; - if bitcopy { - needs_unsafe_bitcopy = true; - break; - } - } - } - Api::RustFunction(efn) if !out.header => { - if efn.throws { - out.include.exception = true; - needs_rust_error = true; - } - for arg in &efn.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - needs_manually_drop = true; - break; - } - } - if let Some(ret) = &efn.ret { - if types.needs_indirect_abi(ret) { - needs_maybe_uninit = true; - } - } - } - _ => {} - } - } - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); - - if needs_rust_string - || needs_rust_str - || needs_rust_slice - || needs_rust_box - || needs_rust_vec - || needs_rust_fn - || needs_rust_error - || needs_rust_isize - || needs_unsafe_bitcopy - || needs_manually_drop - || needs_maybe_uninit - || needs_trycatch - { - writeln!(out, "// #include \"rust/cxx.h\""); - } - - if needs_rust_string { - out.next_section(); - writeln!(out, "struct unsafe_bitcopy_t;"); - } - - write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); - write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); - write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); - write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); - write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); - write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); - write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); - write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); - - if needs_manually_drop { - out.next_section(); - out.include.utility = true; - writeln!(out, "template "); - writeln!(out, "union ManuallyDrop {{"); - writeln!(out, " T value;"); - writeln!( - out, - " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", - ); - writeln!(out, " ~ManuallyDrop() {{}}"); - writeln!(out, "}};"); - } - - if needs_maybe_uninit { - out.next_section(); - writeln!(out, "template "); - writeln!(out, "union MaybeUninit {{"); - writeln!(out, " T value;"); - writeln!(out, " MaybeUninit() {{}}"); - writeln!(out, " ~MaybeUninit() {{}}"); - writeln!(out, "}};"); - } - - out.end_block("namespace cxxbridge02"); - - if needs_trycatch { - out.begin_block("namespace behavior"); - out.include.exception = true; - out.include.type_traits = true; - out.include.utility = true; - writeln!(out, "class missing {{}};"); - writeln!(out, "missing trycatch(...);"); - writeln!(out); - writeln!(out, "template "); - writeln!(out, "static typename std::enable_if<"); - writeln!( - out, - " std::is_same(), std::declval())),", - ); - writeln!(out, " missing>::value>::type"); - writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); - writeln!(out, " func();"); - writeln!(out, "}} catch (const ::std::exception &e) {{"); - writeln!(out, " fail(e.what());"); - writeln!(out, "}}"); - out.end_block("namespace behavior"); - } - - out.end_block("namespace rust"); -} - -fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { - let section = include::get(section); - if needed { - out.next_section(); - for line in section.lines() { - if !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - } -} - -fn write_struct(out: &mut OutFile, strct: &Struct) { - for line in strct.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - writeln!(out, "struct {} final {{", strct.ident); - for field in &strct.fields { - write!(out, " "); - write_type_space(out, &field.ty); - writeln!(out, "{};", field.ident); - } - writeln!(out, "}};"); -} - -fn write_struct_decl(out: &mut OutFile, ident: &Ident) { - writeln!(out, "struct {};", ident); -} - -fn write_struct_using(out: &mut OutFile, ident: &Ident) { - writeln!(out, "using {} = {};", ident, ident); -} - -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - for line in ety.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); - for method in methods { - write!(out, " "); - let sig = &method.sig; - let local_name = method.ident.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); - writeln!(out, ";"); - } - writeln!(out, "}};"); -} - -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { - let mut has_cxx_throws = false; - for api in apis { - if let Api::CxxFunction(efn) = api { - if efn.throws { - has_cxx_throws = true; - break; - } - } - } - - if has_cxx_throws { - out.next_section(); - writeln!( - out, - "const char *cxxbridge02$exception(const char *, size_t);", - ); - } -} - -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - if efn.throws { - write!(out, "::rust::Str::Repr "); - } else { - write_extern_return_type_space(out, &efn.ret, types); - } - let mangled = mangle::extern_fn(&out.namespace, efn); - write!(out, "{}(", mangled); - if let Some(receiver) = &efn.receiver { - if receiver.mutability.is_none() { - write!(out, "const "); - } - write!(out, "{} &self", receiver.ty); - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 || efn.receiver.is_some() { - write!(out, ", "); - } - if arg.ty == RustString { - write!(out, "const "); - } else if let Type::RustVec(_) = arg.ty { - write!(out, "const "); - } - write_extern_arg(out, arg, types); - } - let indirect_return = indirect_return(efn, types); - if indirect_return { - if !efn.args.is_empty() { - write!(out, ", "); - } - write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); - write!(out, "*return$"); - } - writeln!(out, ") noexcept {{"); - write!(out, " "); - write_return_type(out, &efn.ret); - match &efn.receiver { - None => write!(out, "(*{}$)(", efn.ident), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type(out, &arg.ty); - } - write!(out, ")"); - if let Some(receiver) = &efn.receiver { - if receiver.mutability.is_none() { - write!(out, " const"); - } - } - write!(out, " = "); - match &efn.receiver { - None => write!(out, "{}", efn.ident), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), - } - writeln!(out, ";"); - write!(out, " "); - if efn.throws { - writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " ::rust::behavior::trycatch("); - writeln!(out, " [&] {{"); - write!(out, " "); - } - if indirect_return { - write!(out, "new (return$) "); - write_indirect_return_type(out, efn.ret.as_ref().unwrap()); - write!(out, "("); - } else if efn.ret.is_some() { - write!(out, "return "); - } - match &efn.ret { - Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), - Some(Type::SliceRefU8(_)) if !indirect_return => { - write!(out, "::rust::Slice::Repr(") - } - _ => {} - } - match &efn.receiver { - None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self.*{}$)(", efn.ident), - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - if let Type::RustBox(_) = &arg.ty { - write_type(out, &arg.ty); - write!(out, "::from_raw({})", arg.ident); - } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty); - write!(out, "({})", arg.ident); - } else if arg.ty == RustString { - write!( - out, - "::rust::String(::rust::unsafe_bitcopy, *{})", - arg.ident, - ); - } else if let Type::RustVec(_) = arg.ty { - write_type(out, &arg.ty); - write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); - } else if types.needs_indirect_abi(&arg.ty) { - out.include.utility = true; - write!(out, "::std::move(*{})", arg.ident); - } else { - write!(out, "{}", arg.ident); - } - } - write!(out, ")"); - match &efn.ret { - Some(Type::RustBox(_)) => write!(out, ".into_raw()"), - Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::CxxVector(_)) => write!( - out, - " /* Use RVO to convert to r-value and move construct */" - ), - Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), - _ => {} - } - if indirect_return { - write!(out, ")"); - } - writeln!(out, ";"); - if efn.throws { - out.include.cstring = true; - writeln!(out, " throw$.ptr = nullptr;"); - writeln!(out, " }},"); - writeln!(out, " [&](const char *catch$) noexcept {{"); - writeln!(out, " throw$.len = ::std::strlen(catch$);"); - writeln!( - out, - " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", - ); - writeln!(out, " }});"); - writeln!(out, " return throw$;"); - } - writeln!(out, "}}"); - for arg in &efn.args { - if let Type::Fn(f) = &arg.ty { - let var = &arg.ident; - write_function_pointer_trampoline(out, efn, var, f, types); - } - } -} - -fn write_function_pointer_trampoline( - out: &mut OutFile, - efn: &ExternFn, - var: &Ident, - f: &Signature, - types: &Types, -) { - out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); - let indirect_call = true; - write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); - - out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); - write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); -} - -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let link_name = mangle::extern_fn(&out.namespace, efn); - let indirect_call = false; - write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); -} - -fn write_rust_function_decl_impl( - out: &mut OutFile, - link_name: &Symbol, - sig: &Signature, - types: &Types, - indirect_call: bool, -) { - if sig.throws { - write!(out, "::rust::Str::Repr "); - } else { - write_extern_return_type_space(out, &sig.ret, types); - } - write!(out, "{}(", link_name); - let mut needs_comma = false; - if let Some(receiver) = &sig.receiver { - if receiver.mutability.is_none() { - write!(out, "const "); - } - write!(out, "{} &self", receiver.ty); - needs_comma = true; - } - for arg in &sig.args { - if needs_comma { - write!(out, ", "); - } - write_extern_arg(out, arg, types); - needs_comma = true; - } - if indirect_return(sig, types) { - if needs_comma { - write!(out, ", "); - } - write_return_type(out, &sig.ret); - write!(out, "*return$"); - needs_comma = true; - } - if indirect_call { - if needs_comma { - write!(out, ", "); - } - write!(out, "void *"); - } - writeln!(out, ") noexcept;"); -} - -fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - for line in efn.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - let local_name = match &efn.sig.receiver { - None => efn.ident.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), - }; - let invoke = mangle::extern_fn(&out.namespace, efn); - let indirect_call = false; - write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); -} - -fn write_rust_function_shim_decl( - out: &mut OutFile, - local_name: &str, - sig: &Signature, - indirect_call: bool, -) { - write_return_type(out, &sig.ret); - write!(out, "{}(", local_name); - for (i, arg) in sig.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type_space(out, &arg.ty); - write!(out, "{}", arg.ident); - } - if indirect_call { - if !sig.args.is_empty() { - write!(out, ", "); - } - write!(out, "void *extern$"); - } - write!(out, ")"); - if let Some(receiver) = &sig.receiver { - if receiver.mutability.is_none() { - write!(out, " const"); - } - } - if !sig.throws { - write!(out, " noexcept"); - } -} - -fn write_rust_function_shim_impl( - out: &mut OutFile, - local_name: &str, - sig: &Signature, - types: &Types, - invoke: &Symbol, - indirect_call: bool, -) { - if out.header && sig.receiver.is_some() { - // We've already defined this inside the struct. - return; - } - write_rust_function_shim_decl(out, local_name, sig, indirect_call); - if out.header { - writeln!(out, ";"); - return; - } - writeln!(out, " {{"); - for arg in &sig.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - out.include.utility = true; - write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty); - writeln!(out, "> {}$(::std::move({0}));", arg.ident); - } - } - write!(out, " "); - let indirect_return = indirect_return(sig, types); - if indirect_return { - write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap()); - writeln!(out, "> return$;"); - write!(out, " "); - } else if let Some(ret) = &sig.ret { - write!(out, "return "); - match ret { - Type::RustBox(_) => { - write_type(out, ret); - write!(out, "::from_raw("); - } - Type::UniquePtr(_) => { - write_type(out, ret); - write!(out, "("); - } - Type::Ref(_) => write!(out, "*"), - _ => {} - } - } - if sig.throws { - write!(out, "::rust::Str::Repr error$ = "); - } - write!(out, "{}(", invoke); - if sig.receiver.is_some() { - write!(out, "*this"); - } - for (i, arg) in sig.args.iter().enumerate() { - if i > 0 || sig.receiver.is_some() { - write!(out, ", "); - } - match &arg.ty { - Type::Str(_) => write!(out, "::rust::Str::Repr("), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), - ty if types.needs_indirect_abi(ty) => write!(out, "&"), - _ => {} - } - write!(out, "{}", arg.ident); - match &arg.ty { - Type::RustBox(_) => write!(out, ".into_raw()"), - Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), - ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), - _ => {} - } - } - if indirect_return { - if !sig.args.is_empty() { - write!(out, ", "); - } - write!(out, "&return$.value"); - } - if indirect_call { - if !sig.args.is_empty() || indirect_return { - write!(out, ", "); - } - write!(out, "extern$"); - } - write!(out, ")"); - if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { - write!(out, ")"); - } - } - writeln!(out, ";"); - if sig.throws { - writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::Error(error$);"); - writeln!(out, " }}"); - } - if indirect_return { - out.include.utility = true; - writeln!(out, " return ::std::move(return$.value);"); - } - writeln!(out, "}}"); -} - -fn write_return_type(out: &mut OutFile, ty: &Option) { - match ty { - None => write!(out, "void "), - Some(ty) => write_type_space(out, ty), - } -} - -fn indirect_return(sig: &Signature, types: &Types) -> bool { - sig.ret - .as_ref() - .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) -} - -fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::RustBox(ty) | Type::UniquePtr(ty) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Type::Ref(ty) => { - if ty.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &ty.inner); - write!(out, " *"); - } - Type::Str(_) => write!(out, "::rust::Str::Repr"), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), - _ => write_type(out, ty), - } -} - -fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { - write_indirect_return_type(out, ty); - match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} - Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), - _ => write_space_after_type(out, ty), - } -} - -fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { - match ty { - Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Some(Type::Ref(ty)) => { - if ty.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &ty.inner); - write!(out, " *"); - } - Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), - Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), - Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), - _ => write_return_type(out, ty), - } -} - -fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { - match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Type::Str(_) => write!(out, "::rust::Str::Repr "), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), - _ => write_type_space(out, &arg.ty), - } - if types.needs_indirect_abi(&arg.ty) { - write!(out, "*"); - } - write!(out, "{}", arg.ident); -} - -fn write_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), - None => write!(out, "{}", ident), - }, - Type::RustBox(ty) => { - write!(out, "::rust::Box<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::RustVec(ty) => { - write!(out, "::rust::Vec<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::UniquePtr(ptr) => { - write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); - write!(out, ">"); - } - Type::CxxVector(ty) => { - write!(out, "::std::vector<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::Ref(r) => { - if r.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &r.inner); - write!(out, " &"); - } - Type::Slice(_) => { - // For now, only U8 slices are supported, which are covered separately below - unreachable!() - } - Type::Str(_) => { - write!(out, "::rust::Str"); - } - Type::SliceRefU8(_) => { - write!(out, "::rust::Slice"); - } - Type::Fn(f) => { - write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); - match &f.ret { - Some(ret) => write_type(out, ret), - None => write!(out, "void"), - } - write!(out, "("); - for (i, arg) in f.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type(out, &arg.ty); - } - write!(out, ")>"); - } - Type::Void(_) => unreachable!(), - } -} - -fn write_type_space(out: &mut OutFile, ty: &Type) { - write_type(out, ty); - write_space_after_type(out, ty); -} - -fn write_space_after_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::Ident(_) - | Type::RustBox(_) - | Type::UniquePtr(_) - | Type::Str(_) - | Type::CxxVector(_) - | Type::RustVec(_) - | Type::SliceRefU8(_) - | Type::Fn(_) => write!(out, " "), - Type::Ref(_) => {} - Type::Void(_) | Type::Slice(_) => unreachable!(), - } -} - -// Only called for legal referent types of unique_ptr and element types of -// std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += name; - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), - _ => unreachable!(), - } -} - -// Only called for legal referent types of unique_ptr and element types of -// std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), - _ => unreachable!(), - } -} - -fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() - } - - out.begin_block("extern \"C\""); - for ty in types { - if let Type::RustBox(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - out.next_section(); - write_rust_box_extern(out, inner); - } - } else if let Type::RustVec(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - out.next_section(); - write_rust_vec_extern(out, inner); - } - } - } else if let Type::UniquePtr(ptr) = ty { - if let Type::Ident(inner) = &ptr.inner { - if allow_unique_ptr(inner) { - out.next_section(); - write_unique_ptr(out, inner, types); - } - } - } else if let Type::CxxVector(ptr) = ty { - if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() { - out.next_section(); - write_cxx_vector(out, ty, inner, types); - } - } - } - } - out.end_block("extern \"C\""); - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); - for ty in types { - if let Type::RustBox(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, inner); - } - } else if let Type::RustVec(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - write_rust_vec_impl(out, inner); - } - } - } - } - out.end_block("namespace cxxbridge02"); - out.end_block("namespace rust"); -} - -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); - - writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); - writeln!( - out, - "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); -} - -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); - writeln!( - out, - "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", - inner, instance, - ); - writeln!( - out, - "size_t cxxbridge02$rust_vec${}$stride() noexcept;", - instance, - ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); -} - -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); - - writeln!(out, "template <>"); - writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$drop(this);", instance); - writeln!(out, "}}"); -} - -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "template <>"); - writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); - writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); - writeln!( - out, - " return cxxbridge02$rust_vec${}$drop(this);", - instance, - ); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); - writeln!( - out, - " return cxxbridge02$rust_vec${}$data(this);", - instance, - ); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); - writeln!(out, "}}"); -} - -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { - let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); - - writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); - - write_unique_ptr_common(out, &ty, types); - - writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); -} - -// Shared by UniquePtr and UniquePtr>. -fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { - out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); - - let can_construct_from_value = match ty { - Type::Ident(ident) => types.structs.contains_key(ident), - _ => false, - }; - - writeln!( - out, - "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", - inner, - ); - writeln!( - out, - "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", - inner, - ); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, - ); - writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); - writeln!(out, "}}"); - if can_construct_from_value { - writeln!( - out, - "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", - instance, inner, inner, - ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); - } - writeln!( - out, - "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", - instance, inner, inner, - ); - writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); - writeln!(out, "}}"); - writeln!( - out, - "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return ptr.get();"); - writeln!(out, "}}"); - writeln!( - out, - "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return ptr.release();"); - writeln!(out, "}}"); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, - ); - writeln!(out, " ptr->~unique_ptr();"); - writeln!(out, "}}"); -} - -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); - writeln!( - out, - "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", - instance, inner, - ); - writeln!(out, " return s.size();"); - writeln!(out, "}}"); - writeln!( - out, - "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return s[pos];"); - writeln!(out, "}}"); - - write_unique_ptr_common(out, vector_ty, types); - - writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); -} diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 82401ee..c7c48ec 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "Implementation detail of the `cxx` crate." repository = "https://github.com/dtolnay/cxx" +exclude = ["README.md"] [lib] proc-macro = true diff --git a/macro/README.md b/macro/README.md new file mode 100644 index 0000000..b9c1779 --- /dev/null +++ b/macro/README.md @@ -0,0 +1,3 @@ +This directory contains CXX's Rust code generator, which is a procedural macro. +Users won't depend on this crate directly. Instead they'll invoke its macro +through the reexport in the main `cxx` crate. diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 740ab94..0000000 --- a/src/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::error::Error as StdError; -use std::fmt::{self, Display}; -use std::io; - -pub(super) type Result = std::result::Result; - -#[derive(Debug)] -pub(super) enum Error { - MissingOutDir, - TargetDir, - Io(io::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), - Error::TargetDir => write!(f, "failed to locate target dir"), - Error::Io(err) => err.fmt(f), - } - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Error::Io(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) - } -} diff --git a/src/gen b/src/gen deleted file mode 120000 index 334e0fb..0000000 --- a/src/gen +++ /dev/null @@ -1 +0,0 @@ -../gen \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index cff1e88..7e8e9ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,8 +224,7 @@ //! // build.rs //! //! fn main() { -//! cxx::Build::new() -//! .bridge("src/main.rs") // returns a cc::Build +//! cxx_build::bridge("src/main.rs") // returns a cc::Build //! .file("../demo-cxx/demo.cc") //! .flag("-std=c++11") //! .compile("cxxbridge-demo"); @@ -363,18 +362,14 @@ mod concat; mod cxx_string; mod cxx_vector; -mod error; mod exception; mod function; -mod gen; mod opaque; -mod paths; mod result; mod rust_sliceu8; mod rust_str; mod rust_string; mod rust_vec; -mod syntax; mod unique_ptr; mod unwind; @@ -398,107 +393,3 @@ pub mod private { pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } - -use crate::error::Result; -use crate::gen::Opt; -use anyhow::anyhow; -use std::fs; -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -/// The CXX code generator for constructing and compiling C++ code. -/// -/// This is intended to be used from Cargo build scripts to execute CXX's -/// C++ code generator, set up any additional compiler flags depending on -/// the use case, and make the C++ compiler invocation. -/// -///
-/// -/// # Example -/// -/// Example of a canonical Cargo build script that builds a CXX bridge: -/// -/// ```no_run -/// // build.rs -/// -/// fn main() { -/// cxx::Build::new() -/// .bridge("src/main.rs") -/// .file("../demo-cxx/demo.cc") -/// .flag("-std=c++11") -/// .compile("cxxbridge-demo"); -/// -/// println!("cargo:rerun-if-changed=src/main.rs"); -/// println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -/// println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); -/// } -/// ``` -/// -/// A runnable working setup with this build script is shown in the -/// *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. -/// -/// [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -/// -///
-/// -/// # Alternatives -/// -/// For use in non-Cargo builds like Bazel or Buck, CXX provides an -/// alternate way of invoking the C++ code generator as a standalone command -/// line tool. The tool is packaged as the `cxxbridge-cmd` crate. -/// -/// ```bash -/// $ cargo install cxxbridge-cmd # or build it from the repo -/// -/// $ cxxbridge src/main.rs --header > path/to/mybridge.h -/// $ cxxbridge src/main.rs > path/to/mybridge.cc -/// ``` -#[must_use] -pub struct Build { - _private: (), -} - -impl Build { - /// Begin with a [`cc::Build`] in its default configuration. - pub fn new() -> Self { - Build { _private: () } - } - - /// This returns a [`cc::Build`] on which you should continue to set up - /// any additional source files or compiler flags, and lastly call its - /// [`compile`] method to execute the C++ build. - /// - /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile - #[must_use] - pub fn bridge(&self, rust_source_file: impl AsRef) -> cc::Build { - match try_generate_bridge(rust_source_file.as_ref()) { - Ok(build) => build, - Err(err) => { - let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); - process::exit(1); - } - } - } -} - -fn try_generate_bridge(rust_source_file: &Path) -> Result { - let header = gen::do_generate_header(rust_source_file, Opt::default()); - let header_path = paths::out_with_extension(rust_source_file, ".h")?; - fs::create_dir_all(header_path.parent().unwrap())?; - fs::write(&header_path, header)?; - paths::symlink_header(&header_path, rust_source_file); - - let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); - let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; - fs::write(&bridge_path, bridge)?; - let mut build = paths::cc_build(); - build.file(&bridge_path); - - let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); - let _ = fs::create_dir_all(cxx_h.parent().unwrap()); - let _ = fs::remove_file(cxx_h); - let _ = fs::write(cxx_h, gen::include::HEADER); - - Ok(build) -} diff --git a/src/paths.rs b/src/paths.rs deleted file mode 100644 index ca183d9..0000000 --- a/src/paths.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::error::{Error, Result}; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -fn out_dir() -> Result { - env::var_os("OUT_DIR") - .map(PathBuf::from) - .ok_or(Error::MissingOutDir) -} - -pub(crate) fn cc_build() -> cc::Build { - try_cc_build().unwrap_or_default() -} - -fn try_cc_build() -> Result { - let mut build = cc::Build::new(); - build.include(include_dir()?); - build.include(target_dir()?.parent().unwrap()); - Ok(build) -} - -// Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(path: &Path, original: &Path) { - let _ = try_symlink_header(path, original); -} - -fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(original)?; - let ref dst = include_dir()?.join(suffix); - - fs::create_dir_all(dst.parent().unwrap())?; - let _ = fs::remove_file(dst); - symlink_or_copy(path, dst)?; - - let mut file_name = dst.file_name().unwrap().to_os_string(); - file_name.push(".h"); - let ref dst2 = dst.with_file_name(file_name); - symlink_or_copy(path, dst2)?; - - Ok(()) -} - -fn relative_to_parent_of_target_dir(original: &Path) -> Result { - let target_dir = target_dir()?; - let mut outer = target_dir.parent().unwrap(); - let original = canonicalize(original)?; - loop { - if let Ok(suffix) = original.strip_prefix(outer) { - return Ok(suffix.to_owned()); - } - match outer.parent() { - Some(parent) => outer = parent, - None => return Ok(original.components().skip(1).collect()), - } - } -} - -pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { - let mut file_name = path.file_name().unwrap().to_owned(); - file_name.push(ext); - - let out_dir = out_dir()?; - let rel = relative_to_parent_of_target_dir(path)?; - Ok(out_dir.join(rel).with_file_name(file_name)) -} - -pub(crate) fn include_dir() -> Result { - let target_dir = target_dir()?; - Ok(target_dir.join("cxxbridge")) -} - -fn target_dir() -> Result { - let mut dir = out_dir().and_then(canonicalize)?; - loop { - if dir.ends_with("target") { - return Ok(dir); - } - if !dir.pop() { - return Err(Error::TargetDir); - } - } -} - -#[cfg(not(windows))] -fn canonicalize(path: impl AsRef) -> Result { - Ok(fs::canonicalize(path)?) -} - -#[cfg(windows)] -fn canonicalize(path: impl AsRef) -> Result { - // Real fs::canonicalize on Windows produces UNC paths which cl.exe is - // unable to handle in includes. Use a poor approximation instead. - // https://github.com/rust-lang/rust/issues/42869 - // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(env::current_dir()?.join(path)) -} - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_or_copy; - -#[cfg(windows)] -fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { - use std::os::windows::fs::symlink_file; - - // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they - // require Developer Mode. If it fails, fall back to copying the file. - if symlink_file(src, dst).is_err() { - fs::copy(src, dst)?; - } - Ok(()) -} - -#[cfg(not(any(unix, windows)))] -use std::fs::copy as symlink_or_copy; diff --git a/src/syntax b/src/syntax deleted file mode 120000 index f400712..0000000 --- a/src/syntax +++ /dev/null @@ -1 +0,0 @@ -../syntax \ No newline at end of file diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index c84df61..c2d8227 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -11,4 +11,4 @@ path = "lib.rs" cxx = { path = "../.." } [build-dependencies] -cxx = { path = "../.." } +cxx-build = { path = "../../gen/build" } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 5c1bec3..b970362 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,8 +3,7 @@ fn main() { return; } - cxx::Build::new() - .bridge("lib.rs") + cxx_build::bridge("lib.rs") .file("tests.cc") .flag("-std=c++11") .compile("cxx-test-suite"); diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 0bd470b..f4068d9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -67,17 +67,25 @@ dependencies = [ name = "cxx" version = "0.2.12" dependencies = [ - "anyhow", "cc", - "codespan-reporting", + "cxx-build", "cxx-test-suite", "cxxbridge-macro", "link-cplusplus", + "rustversion", + "trybuild", +] + +[[package]] +name = "cxx-build" +version = "0.2.12" +dependencies = [ + "anyhow", + "cc", + "codespan-reporting", "proc-macro2", "quote", - "rustversion", "syn", - "trybuild", ] [[package]] @@ -85,6 +93,7 @@ name = "cxx-test-suite" version = "0.0.0" dependencies = [ "cxx", + "cxx-build", ] [[package]] @@ -104,6 +113,7 @@ name = "cxxbridge-demo" version = "0.0.0" dependencies = [ "cxx", + "cxx-build", ] [[package]] From 171412c0361267c17b6fad0c22b46d62c6922354 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:32:09 +0000 Subject: [PATCH 484/2232] Disable Buck build --- diff --git a/.travis.yml b/.travis.yml index f65f7f8..0243407 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,3 +62,7 @@ matrix: rust: 1.42.0 script: - cargo run --manifest-path demo-rs/Cargo.toml + + # https://github.com/dtolnay/cxx/pull/167 + allow_failures: + - name: Buck From b4714a7b52520426c6b594188924b5b474665edf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:43:29 +0000 Subject: [PATCH 485/2232] Merge pull request #167 from dtolnay/split Split cxx runtime and build components --- diff --git a/.travis.yml b/.travis.yml index 6c0ca54..0243407 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,15 +20,15 @@ matrix: rust: nightly-x86_64-pc-windows-gnu before_script: # windows is bad at symlinks - - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax + - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - name: Windows (msvc) os: windows rust: nightly-x86_64-pc-windows-msvc before_script: - - rm cmd/src/gen cmd/src/syntax gen/include macro/src/syntax src/gen src/syntax - - cp -r include gen; cp -r gen cmd/src; cp -r syntax cmd/src; cp -r syntax macro/src; cp -r gen src; cp -r syntax src + - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax + - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - name: Buck rust: nightly @@ -62,3 +62,7 @@ matrix: rust: 1.42.0 script: - cargo run --manifest-path demo-rs/Cargo.toml + + # https://github.com/dtolnay/cxx/pull/167 + allow_failures: + - name: Buck diff --git a/BUCK b/BUCK index 6a9870e..2339f71 100644 --- a/BUCK +++ b/BUCK @@ -5,19 +5,13 @@ rust_library( deps = [ ":core", ":macro", - "//third-party:anyhow", - "//third-party:cc", - "//third-party:codespan-reporting", "//third-party:link-cplusplus", - "//third-party:proc-macro2", - "//third-party:quote", - "//third-party:syn", ], ) rust_binary( name = "codegen", - srcs = glob(["cmd/src/**"]), + srcs = glob(["gen/cmd/src/**"]), crate = "cxxbridge", visibility = ["PUBLIC"], deps = [ @@ -52,3 +46,17 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "build", + srcs = glob(["gen/build/src/**"]), + visibility = ["PUBLIC"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/BUILD b/BUILD index 2eea8c7..d63fea7 100644 --- a/BUILD +++ b/BUILD @@ -3,25 +3,18 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), - data = ["src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ ":core-lib", ":cxxbridge-macro", - "//third-party:anyhow", - "//third-party:cc", - "//third-party:codespan-reporting", "//third-party:link-cplusplus", - "//third-party:proc-macro2", - "//third-party:quote", - "//third-party:syn", ], ) rust_binary( name = "codegen", - srcs = glob(["cmd/src/**/*.rs"]), - data = ["cmd/src/gen/include/cxx.h"], + srcs = glob(["gen/cmd/src/**/*.rs"]), + data = ["gen/cmd/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", @@ -57,3 +50,18 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "build", + srcs = glob(["gen/build/src/**/*.rs"]), + data = ["gen/build/src/gen/include/cxx.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/Cargo.toml b/Cargo.toml index a47a842..ea97712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,25 +15,20 @@ exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] travis-ci = { repository = "dtolnay/cxx" } [dependencies] -anyhow = "1.0" -cc = "1.0.49" -codespan-reporting = "0.9" cxxbridge-macro = { version = "=0.2.12", path = "macro" } link-cplusplus = "1.0" -proc-macro2 = { version = "1.0", features = ["span-locations"] } -quote = "1.0" -syn = { version = "1.0", features = ["full"] } [build-dependencies] cc = "1.0.49" [dev-dependencies] +cxx-build = { version = "=0.2.12", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.21", features = ["diff"] } [workspace] -members = ["cmd", "demo-rs", "macro", "tests/ffi"] +members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/README.md b/README.md index 7dc7c8b..59bcdac 100644 --- a/README.md +++ b/README.md @@ -219,8 +219,7 @@ set up any additional source files and compiler flags as normal. // build.rs fn main() { - cxx::Build::new() - .bridge("src/main.rs") // returns a cc::Build + cxx_build::bridge("src/main.rs") // returns a cc::Build .file("../demo-cxx/demo.cc") .flag("-std=c++11") .compile("cxxbridge-demo"); diff --git a/cmd/Cargo.toml b/cmd/Cargo.toml deleted file mode 100644 index 742d54b..0000000 --- a/cmd/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "cxxbridge-cmd" -version = "0.2.12" -authors = ["David Tolnay "] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." -repository = "https://github.com/dtolnay/cxx" - -[[bin]] -name = "cxxbridge" -path = "src/main.rs" - -[badges] -travis-ci = { repository = "dtolnay/cxx" } - -[dependencies] -anyhow = "1.0" -codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } -quote = "1.0" -structopt = "0.3" -syn = { version = "1.0", features = ["full"] } - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] diff --git a/cmd/LICENSE-APACHE b/cmd/LICENSE-APACHE deleted file mode 120000 index 965b606..0000000 --- a/cmd/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/cmd/LICENSE-MIT b/cmd/LICENSE-MIT deleted file mode 120000 index 76219eb..0000000 --- a/cmd/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/cmd/src/gen b/cmd/src/gen deleted file mode 120000 index eb22577..0000000 --- a/cmd/src/gen +++ /dev/null @@ -1 +0,0 @@ -../../gen \ No newline at end of file diff --git a/cmd/src/lib.rs b/cmd/src/lib.rs deleted file mode 100644 index 8b1a393..0000000 --- a/cmd/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// empty diff --git a/cmd/src/main.rs b/cmd/src/main.rs deleted file mode 100644 index a20179f..0000000 --- a/cmd/src/main.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![allow( - clippy::cognitive_complexity, - clippy::inherent_to_string, - clippy::large_enum_variant, - clippy::new_without_default, - clippy::toplevel_ref_arg -)] - -mod gen; -mod syntax; - -use gen::include; -use std::io::{self, Write}; -use std::path::PathBuf; -use structopt::StructOpt; - -#[derive(StructOpt, Debug)] -#[structopt( - name = "cxxbridge", - author = "David Tolnay ", - about = "https://github.com/dtolnay/cxx", - usage = "\ - cxxbridge .rs Emit .cc file for bridge to stdout - cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout", - help_message = "Print help information", - version_message = "Print version information" -)] -struct Opt { - /// Input Rust source file containing #[cxx::bridge] - #[structopt(parse(from_os_str), required_unless = "header")] - input: Option, - - /// Emit header with declarations only - #[structopt(long)] - header: bool, - - /// Any additional headers to #include - #[structopt(short, long)] - include: Vec, -} - -fn write(content: impl AsRef<[u8]>) { - let _ = io::stdout().lock().write_all(content.as_ref()); -} - -fn main() { - let opt = Opt::from_args(); - - let gen = gen::Opt { - include: opt.include, - }; - - match (opt.input, opt.header) { - (Some(input), true) => write(gen::do_generate_header(&input, gen)), - (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), - (None, true) => write(include::HEADER), - (None, false) => unreachable!(), // enforced by required_unless - } -} diff --git a/cmd/src/syntax b/cmd/src/syntax deleted file mode 120000 index 83b0080..0000000 --- a/cmd/src/syntax +++ /dev/null @@ -1 +0,0 @@ -../../syntax \ No newline at end of file diff --git a/demo-rs/Cargo.toml b/demo-rs/Cargo.toml index f7e7f84..d2147ab 100644 --- a/demo-rs/Cargo.toml +++ b/demo-rs/Cargo.toml @@ -9,4 +9,4 @@ publish = false cxx = { path = ".." } [build-dependencies] -cxx = { path = ".." } +cxx-build = { path = "../gen/build" } diff --git a/demo-rs/build.rs b/demo-rs/build.rs index 71def71..edbb281 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,6 +1,5 @@ fn main() { - cxx::Build::new() - .bridge("src/main.rs") + cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") .flag("-std=c++11") .compile("cxxbridge-demo"); diff --git a/gen/README.md b/gen/README.md new file mode 100644 index 0000000..9786911 --- /dev/null +++ b/gen/README.md @@ -0,0 +1,4 @@ +This directory contains CXX's C++ code generator. This code generator has two +public frontends, one a command-line application (binary) in the *cmd* directory +and the other a library intended to be used from a build.rs in the *build* +directory. diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml new file mode 100644 index 0000000..3552524 --- /dev/null +++ b/gen/build/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cxx-build" +version = "0.2.12" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into a Cargo build." +repository = "https://github.com/dtolnay/cxx" + +[badges] +travis-ci = { repository = "dtolnay/cxx" } + +[dependencies] +anyhow = "1.0" +cc = "1.0.49" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0", features = ["span-locations"] } +quote = "1.0" +syn = { version = "1.0", features = ["full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/build/LICENSE-APACHE b/gen/build/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/build/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/build/LICENSE-MIT b/gen/build/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/build/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs new file mode 100644 index 0000000..740ab94 --- /dev/null +++ b/gen/build/src/error.rs @@ -0,0 +1,37 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; + +pub(super) type Result = std::result::Result; + +#[derive(Debug)] +pub(super) enum Error { + MissingOutDir, + TargetDir, + Io(io::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), + Error::TargetDir => write!(f, "failed to locate target dir"), + Error::Io(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} diff --git a/gen/build/src/gen b/gen/build/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/build/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs new file mode 100644 index 0000000..5bee67f --- /dev/null +++ b/gen/build/src/lib.rs @@ -0,0 +1,96 @@ +//! The CXX code generator for constructing and compiling C++ code. +//! +//! This is intended to be used from Cargo build scripts to execute CXX's +//! C++ code generator, set up any additional compiler flags depending on +//! the use case, and make the C++ compiler invocation. +//! +//!
+//! +//! # Example +//! +//! Example of a canonical Cargo build script that builds a CXX bridge: +//! +//! ```no_run +//! // build.rs +//! +//! fn main() { +//! cxx_build::bridge("src/main.rs") +//! .file("../demo-cxx/demo.cc") +//! .flag("-std=c++11") +//! .compile("cxxbridge-demo"); +//! +//! println!("cargo:rerun-if-changed=src/main.rs"); +//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); +//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! } +//! ``` +//! +//! A runnable working setup with this build script is shown in the +//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. +//! +//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx +//! +//!
+//! +//! # Alternatives +//! +//! For use in non-Cargo builds like Bazel or Buck, CXX provides an +//! alternate way of invoking the C++ code generator as a standalone command +//! line tool. The tool is packaged as the `cxxbridge-cmd` crate. +//! +//! ```bash +//! $ cargo install cxxbridge-cmd # or build it from the repo +//! +//! $ cxxbridge src/main.rs --header > path/to/mybridge.h +//! $ cxxbridge src/main.rs > path/to/mybridge.cc +//! ``` + +mod error; +mod gen; +mod paths; +mod syntax; + +use crate::error::Result; +use crate::gen::Opt; +use anyhow::anyhow; +use std::fs; +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +/// This returns a [`cc::Build`] on which you should continue to set up any +/// additional source files or compiler flags, and lastly call its [`compile`] +/// method to execute the C++ build. +/// +/// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile +#[must_use] +pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { + match try_generate_bridge(rust_source_file.as_ref()) { + Ok(build) => build, + Err(err) => { + let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); + process::exit(1); + } + } +} + +fn try_generate_bridge(rust_source_file: &Path) -> Result { + let header = gen::do_generate_header(rust_source_file, Opt::default()); + let header_path = paths::out_with_extension(rust_source_file, ".h")?; + fs::create_dir_all(header_path.parent().unwrap())?; + fs::write(&header_path, header)?; + paths::symlink_header(&header_path, rust_source_file); + + let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); + let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; + fs::write(&bridge_path, bridge)?; + let mut build = paths::cc_build(); + build.file(&bridge_path); + + let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); + let _ = fs::create_dir_all(cxx_h.parent().unwrap()); + let _ = fs::remove_file(cxx_h); + let _ = fs::write(cxx_h, gen::include::HEADER); + + Ok(build) +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs new file mode 100644 index 0000000..ca183d9 --- /dev/null +++ b/gen/build/src/paths.rs @@ -0,0 +1,116 @@ +use crate::error::{Error, Result}; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +fn out_dir() -> Result { + env::var_os("OUT_DIR") + .map(PathBuf::from) + .ok_or(Error::MissingOutDir) +} + +pub(crate) fn cc_build() -> cc::Build { + try_cc_build().unwrap_or_default() +} + +fn try_cc_build() -> Result { + let mut build = cc::Build::new(); + build.include(include_dir()?); + build.include(target_dir()?.parent().unwrap()); + Ok(build) +} + +// Symlink the header file into a predictable place. The header generated from +// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. +pub(crate) fn symlink_header(path: &Path, original: &Path) { + let _ = try_symlink_header(path, original); +} + +fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { + let suffix = relative_to_parent_of_target_dir(original)?; + let ref dst = include_dir()?.join(suffix); + + fs::create_dir_all(dst.parent().unwrap())?; + let _ = fs::remove_file(dst); + symlink_or_copy(path, dst)?; + + let mut file_name = dst.file_name().unwrap().to_os_string(); + file_name.push(".h"); + let ref dst2 = dst.with_file_name(file_name); + symlink_or_copy(path, dst2)?; + + Ok(()) +} + +fn relative_to_parent_of_target_dir(original: &Path) -> Result { + let target_dir = target_dir()?; + let mut outer = target_dir.parent().unwrap(); + let original = canonicalize(original)?; + loop { + if let Ok(suffix) = original.strip_prefix(outer) { + return Ok(suffix.to_owned()); + } + match outer.parent() { + Some(parent) => outer = parent, + None => return Ok(original.components().skip(1).collect()), + } + } +} + +pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { + let mut file_name = path.file_name().unwrap().to_owned(); + file_name.push(ext); + + let out_dir = out_dir()?; + let rel = relative_to_parent_of_target_dir(path)?; + Ok(out_dir.join(rel).with_file_name(file_name)) +} + +pub(crate) fn include_dir() -> Result { + let target_dir = target_dir()?; + Ok(target_dir.join("cxxbridge")) +} + +fn target_dir() -> Result { + let mut dir = out_dir().and_then(canonicalize)?; + loop { + if dir.ends_with("target") { + return Ok(dir); + } + if !dir.pop() { + return Err(Error::TargetDir); + } + } +} + +#[cfg(not(windows))] +fn canonicalize(path: impl AsRef) -> Result { + Ok(fs::canonicalize(path)?) +} + +#[cfg(windows)] +fn canonicalize(path: impl AsRef) -> Result { + // Real fs::canonicalize on Windows produces UNC paths which cl.exe is + // unable to handle in includes. Use a poor approximation instead. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + Ok(env::current_dir()?.join(path)) +} + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_or_copy; + +#[cfg(windows)] +fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { + use std::os::windows::fs::symlink_file; + + // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they + // require Developer Mode. If it fails, fall back to copying the file. + if symlink_file(src, dst).is_err() { + fs::copy(src, dst)?; + } + Ok(()) +} + +#[cfg(not(any(unix, windows)))] +use std::fs::copy as symlink_or_copy; diff --git a/gen/build/src/syntax b/gen/build/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/build/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml new file mode 100644 index 0000000..742d54b --- /dev/null +++ b/gen/cmd/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cxxbridge-cmd" +version = "0.2.12" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." +repository = "https://github.com/dtolnay/cxx" + +[[bin]] +name = "cxxbridge" +path = "src/main.rs" + +[badges] +travis-ci = { repository = "dtolnay/cxx" } + +[dependencies] +anyhow = "1.0" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0", features = ["span-locations"] } +quote = "1.0" +structopt = "0.3" +syn = { version = "1.0", features = ["full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/LICENSE-APACHE b/gen/cmd/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/cmd/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/cmd/LICENSE-MIT b/gen/cmd/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/cmd/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/cmd/src/gen b/gen/cmd/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/cmd/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/cmd/src/lib.rs b/gen/cmd/src/lib.rs new file mode 100644 index 0000000..8b1a393 --- /dev/null +++ b/gen/cmd/src/lib.rs @@ -0,0 +1 @@ +// empty diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs new file mode 100644 index 0000000..a20179f --- /dev/null +++ b/gen/cmd/src/main.rs @@ -0,0 +1,60 @@ +#![allow( + clippy::cognitive_complexity, + clippy::inherent_to_string, + clippy::large_enum_variant, + clippy::new_without_default, + clippy::toplevel_ref_arg +)] + +mod gen; +mod syntax; + +use gen::include; +use std::io::{self, Write}; +use std::path::PathBuf; +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt( + name = "cxxbridge", + author = "David Tolnay ", + about = "https://github.com/dtolnay/cxx", + usage = "\ + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit rust/cxx.h header to stdout", + help_message = "Print help information", + version_message = "Print version information" +)] +struct Opt { + /// Input Rust source file containing #[cxx::bridge] + #[structopt(parse(from_os_str), required_unless = "header")] + input: Option, + + /// Emit header with declarations only + #[structopt(long)] + header: bool, + + /// Any additional headers to #include + #[structopt(short, long)] + include: Vec, +} + +fn write(content: impl AsRef<[u8]>) { + let _ = io::stdout().lock().write_all(content.as_ref()); +} + +fn main() { + let opt = Opt::from_args(); + + let gen = gen::Opt { + include: opt.include, + }; + + match (opt.input, opt.header) { + (Some(input), true) => write(gen::do_generate_header(&input, gen)), + (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), + (None, true) => write(include::HEADER), + (None, false) => unreachable!(), // enforced by required_unless + } +} diff --git a/gen/cmd/src/syntax b/gen/cmd/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/cmd/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/error.rs b/gen/error.rs deleted file mode 100644 index 2e8ecc4..0000000 --- a/gen/error.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::syntax; -use anyhow::anyhow; -use codespan_reporting::diagnostic::{Diagnostic, Label}; -use codespan_reporting::files::SimpleFiles; -use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; -use codespan_reporting::term::{self, Config}; -use std::error::Error as StdError; -use std::fmt::{self, Display}; -use std::io::{self, Write}; -use std::ops::Range; -use std::path::Path; -use std::process; - -pub(super) type Result = std::result::Result; - -#[derive(Debug)] -pub(super) enum Error { - NoBridgeMod, - OutOfLineMod, - Io(io::Error), - Syn(syn::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), - Error::Io(err) => err.fmt(f), - Error::Syn(err) => err.fmt(f), - } - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Error::Io(err) => Some(err), - Error::Syn(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: syn::Error) -> Self { - Error::Syn(err) - } -} - -pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { - match error { - Error::Syn(syn_error) => { - let writer = StandardStream::stderr(ColorChoice::Auto); - let ref mut stderr = writer.lock(); - for error in syn_error { - let _ = writeln!(stderr); - display_syn_error(stderr, path, source, error); - } - } - _ => eprintln!("cxxbridge: {:?}", anyhow!(error)), - } - process::exit(1); -} - -fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { - let span = error.span(); - let start = span.start(); - let end = span.end(); - - let mut start_offset = 0; - for _ in 1..start.line { - start_offset += source[start_offset..].find('\n').unwrap() + 1; - } - start_offset += start.column; - - let mut end_offset = start_offset; - if start.line == end.line { - end_offset -= start.column; - } else { - for _ in 0..end.line - start.line { - end_offset += source[end_offset..].find('\n').unwrap() + 1; - } - } - end_offset += end.column; - - let mut files = SimpleFiles::new(); - let file = files.add(path.to_string_lossy(), source); - - let diagnostic = diagnose(file, start_offset..end_offset, error); - - let config = Config::default(); - let _ = term::emit(stderr, &config, &files, &diagnostic); -} - -fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { - let message = error.to_string(); - let info = syntax::error::ERRORS - .iter() - .find(|e| message.contains(e.msg)); - let mut diagnostic = Diagnostic::error().with_message(&message); - let mut label = Label::primary(file, range); - if let Some(info) = info { - label.message = info.label.map_or(message, str::to_owned); - diagnostic.labels.push(label); - diagnostic.notes.extend(info.note.map(str::to_owned)); - } else { - label.message = message; - diagnostic.labels.push(label); - } - diagnostic.code = Some("cxxbridge".to_owned()); - diagnostic -} diff --git a/gen/include b/gen/include deleted file mode 120000 index f5030fe..0000000 --- a/gen/include +++ /dev/null @@ -1 +0,0 @@ -../include \ No newline at end of file diff --git a/gen/include.rs b/gen/include.rs deleted file mode 100644 index 129a8e6..0000000 --- a/gen/include.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::fmt::{self, Display}; - -pub static HEADER: &str = include_str!("include/cxx.h"); - -pub fn get(guard: &str) -> &'static str { - let ifndef = format!("#ifndef {}", guard); - let endif = format!("#endif // {}", guard); - let begin = find_line(&ifndef); - let end = find_line(&endif); - if let (Some(begin), Some(end)) = (begin, end) { - &HEADER[begin..end + endif.len()] - } else { - panic!("not found in cxx.h header: {}", guard) - } -} - -fn find_line(line: &str) -> Option { - let mut offset = 0; - loop { - offset += HEADER[offset..].find(line)?; - let rest = &HEADER[offset + line.len()..]; - if rest.starts_with('\n') || rest.starts_with('\r') { - return Some(offset); - } - offset += line.len(); - } -} - -#[derive(Default, PartialEq)] -pub struct Includes { - custom: Vec, - pub array: bool, - pub cstddef: bool, - pub cstdint: bool, - pub cstring: bool, - pub exception: bool, - pub memory: bool, - pub string: bool, - pub type_traits: bool, - pub utility: bool, - pub vector: bool, - pub base_tsd: bool, -} - -impl Includes { - pub fn new() -> Self { - Includes::default() - } - - pub fn insert(&mut self, include: String) { - self.custom.push(include); - } -} - -impl Extend for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter); - } -} - -impl Display for Includes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for include in &self.custom { - writeln!(f, "#include \"{}\"", include.escape_default())?; - } - if self.array { - writeln!(f, "#include ")?; - } - if self.cstddef { - writeln!(f, "#include ")?; - } - if self.cstdint { - writeln!(f, "#include ")?; - } - if self.cstring { - writeln!(f, "#include ")?; - } - if self.exception { - writeln!(f, "#include ")?; - } - if self.memory { - writeln!(f, "#include ")?; - } - if self.string { - writeln!(f, "#include ")?; - } - if self.type_traits { - writeln!(f, "#include ")?; - } - if self.utility { - writeln!(f, "#include ")?; - } - if self.vector { - writeln!(f, "#include ")?; - } - if self.base_tsd { - writeln!(f, "#if defined(_WIN32)")?; - writeln!(f, "#include ")?; - writeln!(f, "#endif")?; - } - if *self != Self::default() { - writeln!(f)?; - } - Ok(()) - } -} diff --git a/gen/mod.rs b/gen/mod.rs deleted file mode 100644 index 928c6ec..0000000 --- a/gen/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Functionality that is shared between the cxx::generate_bridge entry point and -// the cmd. - -mod error; -pub(super) mod include; -pub(super) mod out; -mod write; - -use self::error::{format_err, Error, Result}; -use crate::syntax::namespace::Namespace; -use crate::syntax::{self, check, Types}; -use quote::quote; -use std::fs; -use std::path::Path; -use syn::{Attribute, File, Item}; - -struct Input { - namespace: Namespace, - module: Vec, -} - -#[derive(Default)] -pub(super) struct Opt { - /// Any additional headers to #include - pub include: Vec, -} - -pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { - let header = false; - generate(path, opt, header) -} - -pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { - let header = true; - generate(path, opt, header) -} - -fn generate(path: &Path, opt: Opt, header: bool) -> Vec { - let source = match fs::read_to_string(path) { - Ok(source) => source, - Err(err) => format_err(path, "", Error::Io(err)), - }; - match (|| -> Result<_> { - let syntax = syn::parse_file(&source)?; - let bridge = find_bridge_mod(syntax)?; - let apis = syntax::parse_items(bridge.module)?; - let types = Types::collect(&apis)?; - check::typecheck(&apis, &types)?; - let out = write::gen(bridge.namespace, &apis, &types, opt, header); - Ok(out) - })() { - Ok(out) => out.content(), - Err(err) => format_err(path, &source, err), - } -} - -fn find_bridge_mod(syntax: File) -> Result { - for item in syntax.items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path; - if quote!(#path).to_string() == "cxx :: bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Input { namespace, module }); - } - } - } - } - Err(Error::NoBridgeMod) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} diff --git a/gen/out.rs b/gen/out.rs deleted file mode 100644 index 08bf85f..0000000 --- a/gen/out.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; -use std::cell::RefCell; -use std::fmt::{self, Arguments, Write}; - -pub(crate) struct OutFile { - pub namespace: Namespace, - pub header: bool, - pub include: Includes, - content: RefCell, -} - -struct Content { - bytes: Vec, - section_pending: bool, - blocks_pending: Vec<&'static str>, -} - -impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { - OutFile { - namespace, - header, - include: Includes::new(), - content: RefCell::new(Content { - bytes: Vec::new(), - section_pending: false, - blocks_pending: Vec::new(), - }), - } - } - - // Write a blank line if the preceding section had any contents. - pub fn next_section(&mut self) { - let content = self.content.get_mut(); - content.section_pending = true; - } - - pub fn begin_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - content.blocks_pending.push(block); - } - - pub fn end_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - if content.blocks_pending.pop().is_none() { - content.bytes.extend_from_slice(b"} // "); - content.bytes.extend_from_slice(block.as_bytes()); - content.bytes.push(b'\n'); - content.section_pending = true; - } - } - - pub fn prepend(&mut self, section: String) { - let content = self.content.get_mut(); - content.bytes.splice(..0, section.into_bytes()); - } - - pub fn write_fmt(&self, args: Arguments) { - let content = &mut *self.content.borrow_mut(); - Write::write_fmt(content, args).unwrap(); - } - - pub fn content(&self) -> Vec { - self.content.borrow().bytes.clone() - } -} - -impl Write for Content { - fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { - if !self.blocks_pending.is_empty() { - if !self.bytes.is_empty() { - self.bytes.push(b'\n'); - } - for block in self.blocks_pending.drain(..) { - self.bytes.extend_from_slice(block.as_bytes()); - self.bytes.extend_from_slice(b" {\n"); - } - self.section_pending = false; - } else if self.section_pending { - if !self.bytes.is_empty() { - self.bytes.push(b'\n'); - } - self.section_pending = false; - } - self.bytes.extend_from_slice(s.as_bytes()); - } - Ok(()) - } -} diff --git a/gen/src/error.rs b/gen/src/error.rs new file mode 100644 index 0000000..2e8ecc4 --- /dev/null +++ b/gen/src/error.rs @@ -0,0 +1,119 @@ +use crate::syntax; +use anyhow::anyhow; +use codespan_reporting::diagnostic::{Diagnostic, Label}; +use codespan_reporting::files::SimpleFiles; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; +use codespan_reporting::term::{self, Config}; +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io::{self, Write}; +use std::ops::Range; +use std::path::Path; +use std::process; + +pub(super) type Result = std::result::Result; + +#[derive(Debug)] +pub(super) enum Error { + NoBridgeMod, + OutOfLineMod, + Io(io::Error), + Syn(syn::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), + Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), + Error::Io(err) => err.fmt(f), + Error::Syn(err) => err.fmt(f), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(err) => Some(err), + Error::Syn(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: syn::Error) -> Self { + Error::Syn(err) + } +} + +pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { + match error { + Error::Syn(syn_error) => { + let writer = StandardStream::stderr(ColorChoice::Auto); + let ref mut stderr = writer.lock(); + for error in syn_error { + let _ = writeln!(stderr); + display_syn_error(stderr, path, source, error); + } + } + _ => eprintln!("cxxbridge: {:?}", anyhow!(error)), + } + process::exit(1); +} + +fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { + let span = error.span(); + let start = span.start(); + let end = span.end(); + + let mut start_offset = 0; + for _ in 1..start.line { + start_offset += source[start_offset..].find('\n').unwrap() + 1; + } + start_offset += start.column; + + let mut end_offset = start_offset; + if start.line == end.line { + end_offset -= start.column; + } else { + for _ in 0..end.line - start.line { + end_offset += source[end_offset..].find('\n').unwrap() + 1; + } + } + end_offset += end.column; + + let mut files = SimpleFiles::new(); + let file = files.add(path.to_string_lossy(), source); + + let diagnostic = diagnose(file, start_offset..end_offset, error); + + let config = Config::default(); + let _ = term::emit(stderr, &config, &files, &diagnostic); +} + +fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { + let message = error.to_string(); + let info = syntax::error::ERRORS + .iter() + .find(|e| message.contains(e.msg)); + let mut diagnostic = Diagnostic::error().with_message(&message); + let mut label = Label::primary(file, range); + if let Some(info) = info { + label.message = info.label.map_or(message, str::to_owned); + diagnostic.labels.push(label); + diagnostic.notes.extend(info.note.map(str::to_owned)); + } else { + label.message = message; + diagnostic.labels.push(label); + } + diagnostic.code = Some("cxxbridge".to_owned()); + diagnostic +} diff --git a/gen/src/include b/gen/src/include new file mode 120000 index 0000000..fcffffb --- /dev/null +++ b/gen/src/include @@ -0,0 +1 @@ +../../include \ No newline at end of file diff --git a/gen/src/include.rs b/gen/src/include.rs new file mode 100644 index 0000000..129a8e6 --- /dev/null +++ b/gen/src/include.rs @@ -0,0 +1,106 @@ +use std::fmt::{self, Display}; + +pub static HEADER: &str = include_str!("include/cxx.h"); + +pub fn get(guard: &str) -> &'static str { + let ifndef = format!("#ifndef {}", guard); + let endif = format!("#endif // {}", guard); + let begin = find_line(&ifndef); + let end = find_line(&endif); + if let (Some(begin), Some(end)) = (begin, end) { + &HEADER[begin..end + endif.len()] + } else { + panic!("not found in cxx.h header: {}", guard) + } +} + +fn find_line(line: &str) -> Option { + let mut offset = 0; + loop { + offset += HEADER[offset..].find(line)?; + let rest = &HEADER[offset + line.len()..]; + if rest.starts_with('\n') || rest.starts_with('\r') { + return Some(offset); + } + offset += line.len(); + } +} + +#[derive(Default, PartialEq)] +pub struct Includes { + custom: Vec, + pub array: bool, + pub cstddef: bool, + pub cstdint: bool, + pub cstring: bool, + pub exception: bool, + pub memory: bool, + pub string: bool, + pub type_traits: bool, + pub utility: bool, + pub vector: bool, + pub base_tsd: bool, +} + +impl Includes { + pub fn new() -> Self { + Includes::default() + } + + pub fn insert(&mut self, include: String) { + self.custom.push(include); + } +} + +impl Extend for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter); + } +} + +impl Display for Includes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for include in &self.custom { + writeln!(f, "#include \"{}\"", include.escape_default())?; + } + if self.array { + writeln!(f, "#include ")?; + } + if self.cstddef { + writeln!(f, "#include ")?; + } + if self.cstdint { + writeln!(f, "#include ")?; + } + if self.cstring { + writeln!(f, "#include ")?; + } + if self.exception { + writeln!(f, "#include ")?; + } + if self.memory { + writeln!(f, "#include ")?; + } + if self.string { + writeln!(f, "#include ")?; + } + if self.type_traits { + writeln!(f, "#include ")?; + } + if self.utility { + writeln!(f, "#include ")?; + } + if self.vector { + writeln!(f, "#include ")?; + } + if self.base_tsd { + writeln!(f, "#if defined(_WIN32)")?; + writeln!(f, "#include ")?; + writeln!(f, "#endif")?; + } + if *self != Self::default() { + writeln!(f)?; + } + Ok(()) + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs new file mode 100644 index 0000000..928c6ec --- /dev/null +++ b/gen/src/mod.rs @@ -0,0 +1,87 @@ +// Functionality that is shared between the cxx::generate_bridge entry point and +// the cmd. + +mod error; +pub(super) mod include; +pub(super) mod out; +mod write; + +use self::error::{format_err, Error, Result}; +use crate::syntax::namespace::Namespace; +use crate::syntax::{self, check, Types}; +use quote::quote; +use std::fs; +use std::path::Path; +use syn::{Attribute, File, Item}; + +struct Input { + namespace: Namespace, + module: Vec, +} + +#[derive(Default)] +pub(super) struct Opt { + /// Any additional headers to #include + pub include: Vec, +} + +pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { + let header = false; + generate(path, opt, header) +} + +pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { + let header = true; + generate(path, opt, header) +} + +fn generate(path: &Path, opt: Opt, header: bool) -> Vec { + let source = match fs::read_to_string(path) { + Ok(source) => source, + Err(err) => format_err(path, "", Error::Io(err)), + }; + match (|| -> Result<_> { + let syntax = syn::parse_file(&source)?; + let bridge = find_bridge_mod(syntax)?; + let apis = syntax::parse_items(bridge.module)?; + let types = Types::collect(&apis)?; + check::typecheck(&apis, &types)?; + let out = write::gen(bridge.namespace, &apis, &types, opt, header); + Ok(out) + })() { + Ok(out) => out.content(), + Err(err) => format_err(path, &source, err), + } +} + +fn find_bridge_mod(syntax: File) -> Result { + for item in syntax.items { + if let Item::Mod(item) = item { + for attr in &item.attrs { + let path = &attr.path; + if quote!(#path).to_string() == "cxx :: bridge" { + let module = match item.content { + Some(module) => module.1, + None => { + return Err(Error::Syn(syn::Error::new_spanned( + item, + Error::OutOfLineMod, + ))); + } + }; + let namespace = parse_args(attr)?; + return Ok(Input { namespace, module }); + } + } + } + } + Err(Error::NoBridgeMod) +} + +fn parse_args(attr: &Attribute) -> syn::Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() + } +} diff --git a/gen/src/out.rs b/gen/src/out.rs new file mode 100644 index 0000000..08bf85f --- /dev/null +++ b/gen/src/out.rs @@ -0,0 +1,91 @@ +use crate::gen::include::Includes; +use crate::syntax::namespace::Namespace; +use std::cell::RefCell; +use std::fmt::{self, Arguments, Write}; + +pub(crate) struct OutFile { + pub namespace: Namespace, + pub header: bool, + pub include: Includes, + content: RefCell, +} + +struct Content { + bytes: Vec, + section_pending: bool, + blocks_pending: Vec<&'static str>, +} + +impl OutFile { + pub fn new(namespace: Namespace, header: bool) -> Self { + OutFile { + namespace, + header, + include: Includes::new(), + content: RefCell::new(Content { + bytes: Vec::new(), + section_pending: false, + blocks_pending: Vec::new(), + }), + } + } + + // Write a blank line if the preceding section had any contents. + pub fn next_section(&mut self) { + let content = self.content.get_mut(); + content.section_pending = true; + } + + pub fn begin_block(&mut self, block: &'static str) { + let content = self.content.get_mut(); + content.blocks_pending.push(block); + } + + pub fn end_block(&mut self, block: &'static str) { + let content = self.content.get_mut(); + if content.blocks_pending.pop().is_none() { + content.bytes.extend_from_slice(b"} // "); + content.bytes.extend_from_slice(block.as_bytes()); + content.bytes.push(b'\n'); + content.section_pending = true; + } + } + + pub fn prepend(&mut self, section: String) { + let content = self.content.get_mut(); + content.bytes.splice(..0, section.into_bytes()); + } + + pub fn write_fmt(&self, args: Arguments) { + let content = &mut *self.content.borrow_mut(); + Write::write_fmt(content, args).unwrap(); + } + + pub fn content(&self) -> Vec { + self.content.borrow().bytes.clone() + } +} + +impl Write for Content { + fn write_str(&mut self, s: &str) -> fmt::Result { + if !s.is_empty() { + if !self.blocks_pending.is_empty() { + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); + } + for block in self.blocks_pending.drain(..) { + self.bytes.extend_from_slice(block.as_bytes()); + self.bytes.extend_from_slice(b" {\n"); + } + self.section_pending = false; + } else if self.section_pending { + if !self.bytes.is_empty() { + self.bytes.push(b'\n'); + } + self.section_pending = false; + } + self.bytes.extend_from_slice(s.as_bytes()); + } + Ok(()) + } +} diff --git a/gen/src/write.rs b/gen/src/write.rs new file mode 100644 index 0000000..b39d978 --- /dev/null +++ b/gen/src/write.rs @@ -0,0 +1,1224 @@ +use crate::gen::out::OutFile; +use crate::gen::{include, Opt}; +use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; +use crate::syntax::symbol::Symbol; +use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use proc_macro2::Ident; +use std::collections::HashMap; + +pub(super) fn gen( + namespace: Namespace, + apis: &[Api], + types: &Types, + opt: Opt, + header: bool, +) -> OutFile { + let mut out_file = OutFile::new(namespace.clone(), header); + let out = &mut out_file; + + if header { + writeln!(out, "#pragma once"); + } + + out.include.extend(opt.include); + for api in apis { + if let Api::Include(include) = api { + out.include.insert(include.value()); + } + } + + write_includes(out, types); + write_include_cxxbridge(out, apis, types); + + out.next_section(); + for name in &namespace { + writeln!(out, "namespace {} {{", name); + } + + out.next_section(); + for api in apis { + match api { + Api::Struct(strct) => write_struct_decl(out, &strct.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident), + _ => {} + } + } + + let mut methods_for_type = HashMap::new(); + for api in apis { + if let Api::RustFunction(efn) = api { + if let Some(receiver) = &efn.sig.receiver { + methods_for_type + .entry(&receiver.ty) + .or_insert_with(Vec::new) + .push(efn); + } + } + } + + for api in apis { + match api { + Api::Struct(strct) => { + out.next_section(); + write_struct(out, strct); + } + Api::RustType(ety) => { + if let Some(methods) = methods_for_type.get(&ety.ident) { + out.next_section(); + write_struct_with_methods(out, ety, methods); + } + } + _ => {} + } + } + + if !header { + out.begin_block("extern \"C\""); + write_exception_glue(out, apis); + for api in apis { + let (efn, write): (_, fn(_, _, _)) = match api { + Api::CxxFunction(efn) => (efn, write_cxx_function_shim), + Api::RustFunction(efn) => (efn, write_rust_function_decl), + _ => continue, + }; + out.next_section(); + write(out, efn, types); + } + out.end_block("extern \"C\""); + } + + for api in apis { + if let Api::RustFunction(efn) = api { + out.next_section(); + write_rust_function_shim(out, efn, types); + } + } + + out.next_section(); + for name in namespace.iter().rev() { + writeln!(out, "}} // namespace {}", name); + } + + if !header { + out.next_section(); + write_generic_instantiations(out, types); + } + + out.prepend(out.include.to_string()); + + out_file +} + +fn write_includes(out: &mut OutFile, types: &Types) { + for ty in types { + match ty { + Type::Ident(ident) => match Atom::from(ident) { + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) + | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, + Some(CxxString) => out.include.string = true, + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} + }, + Type::RustBox(_) => out.include.type_traits = true, + Type::UniquePtr(_) => out.include.memory = true, + Type::CxxVector(_) => out.include.vector = true, + Type::SliceRefU8(_) => out.include.cstdint = true, + _ => {} + } + } +} + +fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { + let mut needs_rust_string = false; + let mut needs_rust_str = false; + let mut needs_rust_slice = false; + let mut needs_rust_box = false; + let mut needs_rust_vec = false; + let mut needs_rust_fn = false; + let mut needs_rust_isize = false; + for ty in types { + match ty { + Type::RustBox(_) => { + out.include.type_traits = true; + needs_rust_box = true; + } + Type::RustVec(_) => { + out.include.array = true; + out.include.type_traits = true; + needs_rust_vec = true; + } + Type::Str(_) => { + out.include.cstdint = true; + out.include.string = true; + needs_rust_str = true; + } + Type::Fn(_) => { + needs_rust_fn = true; + } + Type::Slice(_) | Type::SliceRefU8(_) => { + needs_rust_slice = true; + } + ty if ty == Isize => { + out.include.base_tsd = true; + needs_rust_isize = true; + } + ty if ty == RustString => { + out.include.array = true; + out.include.cstdint = true; + out.include.string = true; + needs_rust_string = true; + } + _ => {} + } + } + + let mut needs_rust_error = false; + let mut needs_unsafe_bitcopy = false; + let mut needs_manually_drop = false; + let mut needs_maybe_uninit = false; + let mut needs_trycatch = false; + for api in apis { + match api { + Api::CxxFunction(efn) if !out.header => { + if efn.throws { + needs_trycatch = true; + } + for arg in &efn.args { + let bitcopy = match arg.ty { + Type::RustVec(_) => true, + _ => arg.ty == RustString, + }; + if bitcopy { + needs_unsafe_bitcopy = true; + break; + } + } + } + Api::RustFunction(efn) if !out.header => { + if efn.throws { + out.include.exception = true; + needs_rust_error = true; + } + for arg in &efn.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + needs_manually_drop = true; + break; + } + } + if let Some(ret) = &efn.ret { + if types.needs_indirect_abi(ret) { + needs_maybe_uninit = true; + } + } + } + _ => {} + } + } + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge02"); + + if needs_rust_string + || needs_rust_str + || needs_rust_slice + || needs_rust_box + || needs_rust_vec + || needs_rust_fn + || needs_rust_error + || needs_rust_isize + || needs_unsafe_bitcopy + || needs_manually_drop + || needs_maybe_uninit + || needs_trycatch + { + writeln!(out, "// #include \"rust/cxx.h\""); + } + + if needs_rust_string { + out.next_section(); + writeln!(out, "struct unsafe_bitcopy_t;"); + } + + write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); + write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); + write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); + write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); + write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); + write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); + write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); + write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); + + if needs_manually_drop { + out.next_section(); + out.include.utility = true; + writeln!(out, "template "); + writeln!(out, "union ManuallyDrop {{"); + writeln!(out, " T value;"); + writeln!( + out, + " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", + ); + writeln!(out, " ~ManuallyDrop() {{}}"); + writeln!(out, "}};"); + } + + if needs_maybe_uninit { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union MaybeUninit {{"); + writeln!(out, " T value;"); + writeln!(out, " MaybeUninit() {{}}"); + writeln!(out, " ~MaybeUninit() {{}}"); + writeln!(out, "}};"); + } + + out.end_block("namespace cxxbridge02"); + + if needs_trycatch { + out.begin_block("namespace behavior"); + out.include.exception = true; + out.include.type_traits = true; + out.include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); + writeln!(out); + writeln!(out, "template "); + writeln!(out, "static typename std::enable_if<"); + writeln!( + out, + " std::is_same(), std::declval())),", + ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + out.end_block("namespace behavior"); + } + + out.end_block("namespace rust"); +} + +fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { + let section = include::get(section); + if needed { + out.next_section(); + for line in section.lines() { + if !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + } +} + +fn write_struct(out: &mut OutFile, strct: &Struct) { + for line in strct.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", strct.ident); + for field in &strct.fields { + write!(out, " "); + write_type_space(out, &field.ty); + writeln!(out, "{};", field.ident); + } + writeln!(out, "}};"); +} + +fn write_struct_decl(out: &mut OutFile, ident: &Ident) { + writeln!(out, "struct {};", ident); +} + +fn write_struct_using(out: &mut OutFile, ident: &Ident) { + writeln!(out, "using {} = {};", ident, ident); +} + +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { + for line in ety.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "struct {} final {{", ety.ident); + writeln!(out, " {}() = delete;", ety.ident); + writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + for method in methods { + write!(out, " "); + let sig = &method.sig; + let local_name = method.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, false); + writeln!(out, ";"); + } + writeln!(out, "}};"); +} + +fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { + let mut has_cxx_throws = false; + for api in apis { + if let Api::CxxFunction(efn) = api { + if efn.throws { + has_cxx_throws = true; + break; + } + } + } + + if has_cxx_throws { + out.next_section(); + writeln!( + out, + "const char *cxxbridge02$exception(const char *, size_t);", + ); + } +} + +fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { + if efn.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type_space(out, &efn.ret, types); + } + let mangled = mangle::extern_fn(&out.namespace, efn); + write!(out, "{}(", mangled); + if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } + write!(out, "{} &self", receiver.ty); + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 || efn.receiver.is_some() { + write!(out, ", "); + } + if arg.ty == RustString { + write!(out, "const "); + } else if let Type::RustVec(_) = arg.ty { + write!(out, "const "); + } + write_extern_arg(out, arg, types); + } + let indirect_return = indirect_return(efn, types); + if indirect_return { + if !efn.args.is_empty() { + write!(out, ", "); + } + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); + write!(out, "*return$"); + } + writeln!(out, ") noexcept {{"); + write!(out, " "); + write_return_type(out, &efn.ret); + match &efn.receiver { + None => write!(out, "(*{}$)(", efn.ident), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")"); + if let Some(receiver) = &efn.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } + } + write!(out, " = "); + match &efn.receiver { + None => write!(out, "{}", efn.ident), + Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), + } + writeln!(out, ";"); + write!(out, " "); + if efn.throws { + writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, " ::rust::behavior::trycatch("); + writeln!(out, " [&] {{"); + write!(out, " "); + } + if indirect_return { + write!(out, "new (return$) "); + write_indirect_return_type(out, efn.ret.as_ref().unwrap()); + write!(out, "("); + } else if efn.ret.is_some() { + write!(out, "return "); + } + match &efn.ret { + Some(Type::Ref(_)) => write!(out, "&"), + Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), + Some(Type::SliceRefU8(_)) if !indirect_return => { + write!(out, "::rust::Slice::Repr(") + } + _ => {} + } + match &efn.receiver { + None => write!(out, "{}$(", efn.ident), + Some(_) => write!(out, "(self.*{}$)(", efn.ident), + } + for (i, arg) in efn.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + if let Type::RustBox(_) = &arg.ty { + write_type(out, &arg.ty); + write!(out, "::from_raw({})", arg.ident); + } else if let Type::UniquePtr(_) = &arg.ty { + write_type(out, &arg.ty); + write!(out, "({})", arg.ident); + } else if arg.ty == RustString { + write!( + out, + "::rust::String(::rust::unsafe_bitcopy, *{})", + arg.ident, + ); + } else if let Type::RustVec(_) = arg.ty { + write_type(out, &arg.ty); + write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); + } else if types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; + write!(out, "::std::move(*{})", arg.ident); + } else { + write!(out, "{}", arg.ident); + } + } + write!(out, ")"); + match &efn.ret { + Some(Type::RustBox(_)) => write!(out, ".into_raw()"), + Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::CxxVector(_)) => write!( + out, + " /* Use RVO to convert to r-value and move construct */" + ), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), + _ => {} + } + if indirect_return { + write!(out, ")"); + } + writeln!(out, ";"); + if efn.throws { + out.include.cstring = true; + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }},"); + writeln!(out, " [&](const char *catch$) noexcept {{"); + writeln!(out, " throw$.len = ::std::strlen(catch$);"); + writeln!( + out, + " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", + ); + writeln!(out, " }});"); + writeln!(out, " return throw$;"); + } + writeln!(out, "}}"); + for arg in &efn.args { + if let Type::Fn(f) = &arg.ty { + let var = &arg.ident; + write_function_pointer_trampoline(out, efn, var, f, types); + } + } +} + +fn write_function_pointer_trampoline( + out: &mut OutFile, + efn: &ExternFn, + var: &Ident, + f: &Signature, + types: &Types, +) { + out.next_section(); + let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let indirect_call = true; + write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); + + out.next_section(); + let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); +} + +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { + let link_name = mangle::extern_fn(&out.namespace, efn); + let indirect_call = false; + write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); +} + +fn write_rust_function_decl_impl( + out: &mut OutFile, + link_name: &Symbol, + sig: &Signature, + types: &Types, + indirect_call: bool, +) { + if sig.throws { + write!(out, "::rust::Str::Repr "); + } else { + write_extern_return_type_space(out, &sig.ret, types); + } + write!(out, "{}(", link_name); + let mut needs_comma = false; + if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, "const "); + } + write!(out, "{} &self", receiver.ty); + needs_comma = true; + } + for arg in &sig.args { + if needs_comma { + write!(out, ", "); + } + write_extern_arg(out, arg, types); + needs_comma = true; + } + if indirect_return(sig, types) { + if needs_comma { + write!(out, ", "); + } + write_return_type(out, &sig.ret); + write!(out, "*return$"); + needs_comma = true; + } + if indirect_call { + if needs_comma { + write!(out, ", "); + } + write!(out, "void *"); + } + writeln!(out, ") noexcept;"); +} + +fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { + for line in efn.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + let local_name = match &efn.sig.receiver { + None => efn.ident.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), + }; + let invoke = mangle::extern_fn(&out.namespace, efn); + let indirect_call = false; + write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); +} + +fn write_rust_function_shim_decl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + indirect_call: bool, +) { + write_return_type(out, &sig.ret); + write!(out, "{}(", local_name); + for (i, arg) in sig.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type_space(out, &arg.ty); + write!(out, "{}", arg.ident); + } + if indirect_call { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "void *extern$"); + } + write!(out, ")"); + if let Some(receiver) = &sig.receiver { + if receiver.mutability.is_none() { + write!(out, " const"); + } + } + if !sig.throws { + write!(out, " noexcept"); + } +} + +fn write_rust_function_shim_impl( + out: &mut OutFile, + local_name: &str, + sig: &Signature, + types: &Types, + invoke: &Symbol, + indirect_call: bool, +) { + if out.header && sig.receiver.is_some() { + // We've already defined this inside the struct. + return; + } + write_rust_function_shim_decl(out, local_name, sig, indirect_call); + if out.header { + writeln!(out, ";"); + return; + } + writeln!(out, " {{"); + for arg in &sig.args { + if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + out.include.utility = true; + write!(out, " ::rust::ManuallyDrop<"); + write_type(out, &arg.ty); + writeln!(out, "> {}$(::std::move({0}));", arg.ident); + } + } + write!(out, " "); + let indirect_return = indirect_return(sig, types); + if indirect_return { + write!(out, "::rust::MaybeUninit<"); + write_type(out, sig.ret.as_ref().unwrap()); + writeln!(out, "> return$;"); + write!(out, " "); + } else if let Some(ret) = &sig.ret { + write!(out, "return "); + match ret { + Type::RustBox(_) => { + write_type(out, ret); + write!(out, "::from_raw("); + } + Type::UniquePtr(_) => { + write_type(out, ret); + write!(out, "("); + } + Type::Ref(_) => write!(out, "*"), + _ => {} + } + } + if sig.throws { + write!(out, "::rust::Str::Repr error$ = "); + } + write!(out, "{}(", invoke); + if sig.receiver.is_some() { + write!(out, "*this"); + } + for (i, arg) in sig.args.iter().enumerate() { + if i > 0 || sig.receiver.is_some() { + write!(out, ", "); + } + match &arg.ty { + Type::Str(_) => write!(out, "::rust::Str::Repr("), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), + ty if types.needs_indirect_abi(ty) => write!(out, "&"), + _ => {} + } + write!(out, "{}", arg.ident); + match &arg.ty { + Type::RustBox(_) => write!(out, ".into_raw()"), + Type::UniquePtr(_) => write!(out, ".release()"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), + ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), + _ => {} + } + } + if indirect_return { + if !sig.args.is_empty() { + write!(out, ", "); + } + write!(out, "&return$.value"); + } + if indirect_call { + if !sig.args.is_empty() || indirect_return { + write!(out, ", "); + } + write!(out, "extern$"); + } + write!(out, ")"); + if let Some(ret) = &sig.ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + write!(out, ")"); + } + } + writeln!(out, ";"); + if sig.throws { + writeln!(out, " if (error$.ptr) {{"); + writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " }}"); + } + if indirect_return { + out.include.utility = true; + writeln!(out, " return ::std::move(return$.value);"); + } + writeln!(out, "}}"); +} + +fn write_return_type(out: &mut OutFile, ty: &Option) { + match ty { + None => write!(out, "void "), + Some(ty) => write_type_space(out, ty), + } +} + +fn indirect_return(sig: &Signature, types: &Types) -> bool { + sig.ret + .as_ref() + .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) +} + +fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::RustBox(ty) | Type::UniquePtr(ty) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Type::Ref(ty) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } + Type::Str(_) => write!(out, "::rust::Str::Repr"), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), + _ => write_type(out, ty), + } +} + +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { + write_indirect_return_type(out, ty); + match ty { + Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} + Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), + _ => write_space_after_type(out, ty), + } +} + +fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { + match ty { + Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Some(Type::Ref(ty)) => { + if ty.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &ty.inner); + write!(out, " *"); + } + Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), + Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), + Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), + _ => write_return_type(out, ty), + } +} + +fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { + match &arg.ty { + Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { + write_type_space(out, &ty.inner); + write!(out, "*"); + } + Type::Str(_) => write!(out, "::rust::Str::Repr "), + Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), + _ => write_type_space(out, &arg.ty), + } + if types.needs_indirect_abi(&arg.ty) { + write!(out, "*"); + } + write!(out, "{}", arg.ident); +} + +fn write_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::Ident(ident) => match Atom::from(ident) { + Some(Bool) => write!(out, "bool"), + Some(U8) => write!(out, "uint8_t"), + Some(U16) => write!(out, "uint16_t"), + Some(U32) => write!(out, "uint32_t"), + Some(U64) => write!(out, "uint64_t"), + Some(Usize) => write!(out, "size_t"), + Some(I8) => write!(out, "int8_t"), + Some(I16) => write!(out, "int16_t"), + Some(I32) => write!(out, "int32_t"), + Some(I64) => write!(out, "int64_t"), + Some(Isize) => write!(out, "::rust::isize"), + Some(F32) => write!(out, "float"), + Some(F64) => write!(out, "double"), + Some(CxxString) => write!(out, "::std::string"), + Some(RustString) => write!(out, "::rust::String"), + None => write!(out, "{}", ident), + }, + Type::RustBox(ty) => { + write!(out, "::rust::Box<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::RustVec(ty) => { + write!(out, "::rust::Vec<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::UniquePtr(ptr) => { + write!(out, "::std::unique_ptr<"); + write_type(out, &ptr.inner); + write!(out, ">"); + } + Type::CxxVector(ty) => { + write!(out, "::std::vector<"); + write_type(out, &ty.inner); + write!(out, ">"); + } + Type::Ref(r) => { + if r.mutability.is_none() { + write!(out, "const "); + } + write_type(out, &r.inner); + write!(out, " &"); + } + Type::Slice(_) => { + // For now, only U8 slices are supported, which are covered separately below + unreachable!() + } + Type::Str(_) => { + write!(out, "::rust::Str"); + } + Type::SliceRefU8(_) => { + write!(out, "::rust::Slice"); + } + Type::Fn(f) => { + write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); + match &f.ret { + Some(ret) => write_type(out, ret), + None => write!(out, "void"), + } + write!(out, "("); + for (i, arg) in f.args.iter().enumerate() { + if i > 0 { + write!(out, ", "); + } + write_type(out, &arg.ty); + } + write!(out, ")>"); + } + Type::Void(_) => unreachable!(), + } +} + +fn write_type_space(out: &mut OutFile, ty: &Type) { + write_type(out, ty); + write_space_after_type(out, ty); +} + +fn write_space_after_type(out: &mut OutFile, ty: &Type) { + match ty { + Type::Ident(_) + | Type::RustBox(_) + | Type::UniquePtr(_) + | Type::Str(_) + | Type::CxxVector(_) + | Type::RustVec(_) + | Type::SliceRefU8(_) + | Type::Fn(_) => write!(out, " "), + Type::Ref(_) => {} + Type::Void(_) | Type::Slice(_) => unreachable!(), + } +} + +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_typename(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(ident) => { + let mut path = String::new(); + for name in namespace { + path += name; + path += "::"; + } + path += &ident.to_string(); + path + } + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + _ => unreachable!(), + } +} + +// Only called for legal referent types of unique_ptr and element types of +// std::vector and Vec. +fn to_mangled(namespace: &Namespace, ty: &Type) -> String { + match ty { + Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), + Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + _ => unreachable!(), + } +} + +fn write_generic_instantiations(out: &mut OutFile, types: &Types) { + fn allow_unique_ptr(ident: &Ident) -> bool { + Atom::from(ident).is_none() + } + + out.begin_block("extern \"C\""); + for ty in types { + if let Type::RustBox(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + out.next_section(); + write_rust_box_extern(out, inner); + } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_rust_vec_extern(out, inner); + } + } + } else if let Type::UniquePtr(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if allow_unique_ptr(inner) { + out.next_section(); + write_unique_ptr(out, inner, types); + } + } + } else if let Type::CxxVector(ptr) = ty { + if let Type::Ident(inner) = &ptr.inner { + if Atom::from(inner).is_none() { + out.next_section(); + write_cxx_vector(out, ty, inner, types); + } + } + } + } + out.end_block("extern \"C\""); + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge02"); + for ty in types { + if let Type::RustBox(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + write_rust_box_impl(out, inner); + } + } else if let Type::RustVec(ty) = ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + write_rust_vec_impl(out, inner); + } + } + } + } + out.end_block("namespace cxxbridge02"); + out.end_block("namespace rust"); +} + +fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + for name in &out.namespace { + inner += name; + inner += "::"; + } + inner += &ident.to_string(); + let instance = inner.replace("::", "$"); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!( + out, + "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); +} + +fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!( + out, + "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + instance, inner, + ); + writeln!( + out, + "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + inner, instance, + ); + writeln!( + out, + "size_t cxxbridge02$rust_vec${}$stride() noexcept;", + instance, + ); + writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); +} + +fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { + let mut inner = String::new(); + for name in &out.namespace { + inner += name; + inner += "::"; + } + inner += &ident.to_string(); + let instance = inner.replace("::", "$"); + + writeln!(out, "template <>"); + writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); + writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "void Box<{}>::drop() noexcept {{", inner); + writeln!(out, " cxxbridge02$box${}$drop(this);", instance); + writeln!(out, "}}"); +} + +fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "template <>"); + writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); + writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$drop(this);", + instance, + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); + writeln!( + out, + " return cxxbridge02$rust_vec${}$data(this);", + instance, + ); + writeln!(out, "}}"); + + writeln!(out, "template <>"); + writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); + writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); + writeln!(out, "}}"); +} + +fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { + let ty = Type::Ident(ident.clone()); + let instance = to_mangled(&out.namespace, &ty); + + writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + + write_unique_ptr_common(out, &ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); +} + +// Shared by UniquePtr and UniquePtr>. +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.utility = true; + let inner = to_typename(&out.namespace, ty); + let instance = to_mangled(&out.namespace, ty); + + let can_construct_from_value = match ty { + Type::Ident(ident) => types.structs.contains_key(ident), + _ => false, + }; + + writeln!( + out, + "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", + inner, + ); + writeln!( + out, + "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", + inner, + ); + writeln!( + out, + "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + instance, inner, + ); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); + writeln!(out, "}}"); + if can_construct_from_value { + writeln!( + out, + "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + instance, inner, inner, + ); + writeln!( + out, + " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", + inner, inner, + ); + writeln!(out, "}}"); + } + writeln!( + out, + "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + instance, inner, inner, + ); + writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); + writeln!(out, "}}"); + writeln!( + out, + "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return ptr.get();"); + writeln!(out, "}}"); + writeln!( + out, + "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return ptr.release();"); + writeln!(out, "}}"); + writeln!( + out, + "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + instance, inner, + ); + writeln!(out, " ptr->~unique_ptr();"); + writeln!(out, "}}"); +} + +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { + let element = Type::Ident(element.clone()); + let inner = to_typename(&out.namespace, &element); + let instance = to_mangled(&out.namespace, &element); + + writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); + writeln!( + out, + "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", + instance, inner, + ); + writeln!(out, " return s.size();"); + writeln!(out, "}}"); + writeln!( + out, + "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + inner, instance, inner, + ); + writeln!(out, " return s[pos];"); + writeln!(out, "}}"); + + write_unique_ptr_common(out, vector_ty, types); + + writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); +} diff --git a/gen/write.rs b/gen/write.rs deleted file mode 100644 index b39d978..0000000 --- a/gen/write.rs +++ /dev/null @@ -1,1224 +0,0 @@ -use crate::gen::out::OutFile; -use crate::gen::{include, Opt}; -use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; -use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; -use proc_macro2::Ident; -use std::collections::HashMap; - -pub(super) fn gen( - namespace: Namespace, - apis: &[Api], - types: &Types, - opt: Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); - let out = &mut out_file; - - if header { - writeln!(out, "#pragma once"); - } - - out.include.extend(opt.include); - for api in apis { - if let Api::Include(include) = api { - out.include.insert(include.value()); - } - } - - write_includes(out, types); - write_include_cxxbridge(out, apis, types); - - out.next_section(); - for name in &namespace { - writeln!(out, "namespace {} {{", name); - } - - out.next_section(); - for api in apis { - match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), - _ => {} - } - } - - let mut methods_for_type = HashMap::new(); - for api in apis { - if let Api::RustFunction(efn) = api { - if let Some(receiver) = &efn.sig.receiver { - methods_for_type - .entry(&receiver.ty) - .or_insert_with(Vec::new) - .push(efn); - } - } - } - - for api in apis { - match api { - Api::Struct(strct) => { - out.next_section(); - write_struct(out, strct); - } - Api::RustType(ety) => { - if let Some(methods) = methods_for_type.get(&ety.ident) { - out.next_section(); - write_struct_with_methods(out, ety, methods); - } - } - _ => {} - } - } - - if !header { - out.begin_block("extern \"C\""); - write_exception_glue(out, apis); - for api in apis { - let (efn, write): (_, fn(_, _, _)) = match api { - Api::CxxFunction(efn) => (efn, write_cxx_function_shim), - Api::RustFunction(efn) => (efn, write_rust_function_decl), - _ => continue, - }; - out.next_section(); - write(out, efn, types); - } - out.end_block("extern \"C\""); - } - - for api in apis { - if let Api::RustFunction(efn) = api { - out.next_section(); - write_rust_function_shim(out, efn, types); - } - } - - out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - - if !header { - out.next_section(); - write_generic_instantiations(out, types); - } - - out.prepend(out.include.to_string()); - - out_file -} - -fn write_includes(out: &mut OutFile, types: &Types) { - for ty in types { - match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) - | Some(I64) => out.include.cstdint = true, - Some(Usize) => out.include.cstddef = true, - Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} - }, - Type::RustBox(_) => out.include.type_traits = true, - Type::UniquePtr(_) => out.include.memory = true, - Type::CxxVector(_) => out.include.vector = true, - Type::SliceRefU8(_) => out.include.cstdint = true, - _ => {} - } - } -} - -fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { - let mut needs_rust_string = false; - let mut needs_rust_str = false; - let mut needs_rust_slice = false; - let mut needs_rust_box = false; - let mut needs_rust_vec = false; - let mut needs_rust_fn = false; - let mut needs_rust_isize = false; - for ty in types { - match ty { - Type::RustBox(_) => { - out.include.type_traits = true; - needs_rust_box = true; - } - Type::RustVec(_) => { - out.include.array = true; - out.include.type_traits = true; - needs_rust_vec = true; - } - Type::Str(_) => { - out.include.cstdint = true; - out.include.string = true; - needs_rust_str = true; - } - Type::Fn(_) => { - needs_rust_fn = true; - } - Type::Slice(_) | Type::SliceRefU8(_) => { - needs_rust_slice = true; - } - ty if ty == Isize => { - out.include.base_tsd = true; - needs_rust_isize = true; - } - ty if ty == RustString => { - out.include.array = true; - out.include.cstdint = true; - out.include.string = true; - needs_rust_string = true; - } - _ => {} - } - } - - let mut needs_rust_error = false; - let mut needs_unsafe_bitcopy = false; - let mut needs_manually_drop = false; - let mut needs_maybe_uninit = false; - let mut needs_trycatch = false; - for api in apis { - match api { - Api::CxxFunction(efn) if !out.header => { - if efn.throws { - needs_trycatch = true; - } - for arg in &efn.args { - let bitcopy = match arg.ty { - Type::RustVec(_) => true, - _ => arg.ty == RustString, - }; - if bitcopy { - needs_unsafe_bitcopy = true; - break; - } - } - } - Api::RustFunction(efn) if !out.header => { - if efn.throws { - out.include.exception = true; - needs_rust_error = true; - } - for arg in &efn.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - needs_manually_drop = true; - break; - } - } - if let Some(ret) = &efn.ret { - if types.needs_indirect_abi(ret) { - needs_maybe_uninit = true; - } - } - } - _ => {} - } - } - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); - - if needs_rust_string - || needs_rust_str - || needs_rust_slice - || needs_rust_box - || needs_rust_vec - || needs_rust_fn - || needs_rust_error - || needs_rust_isize - || needs_unsafe_bitcopy - || needs_manually_drop - || needs_maybe_uninit - || needs_trycatch - { - writeln!(out, "// #include \"rust/cxx.h\""); - } - - if needs_rust_string { - out.next_section(); - writeln!(out, "struct unsafe_bitcopy_t;"); - } - - write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); - write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); - write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); - write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); - write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); - write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); - write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); - write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); - - if needs_manually_drop { - out.next_section(); - out.include.utility = true; - writeln!(out, "template "); - writeln!(out, "union ManuallyDrop {{"); - writeln!(out, " T value;"); - writeln!( - out, - " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", - ); - writeln!(out, " ~ManuallyDrop() {{}}"); - writeln!(out, "}};"); - } - - if needs_maybe_uninit { - out.next_section(); - writeln!(out, "template "); - writeln!(out, "union MaybeUninit {{"); - writeln!(out, " T value;"); - writeln!(out, " MaybeUninit() {{}}"); - writeln!(out, " ~MaybeUninit() {{}}"); - writeln!(out, "}};"); - } - - out.end_block("namespace cxxbridge02"); - - if needs_trycatch { - out.begin_block("namespace behavior"); - out.include.exception = true; - out.include.type_traits = true; - out.include.utility = true; - writeln!(out, "class missing {{}};"); - writeln!(out, "missing trycatch(...);"); - writeln!(out); - writeln!(out, "template "); - writeln!(out, "static typename std::enable_if<"); - writeln!( - out, - " std::is_same(), std::declval())),", - ); - writeln!(out, " missing>::value>::type"); - writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); - writeln!(out, " func();"); - writeln!(out, "}} catch (const ::std::exception &e) {{"); - writeln!(out, " fail(e.what());"); - writeln!(out, "}}"); - out.end_block("namespace behavior"); - } - - out.end_block("namespace rust"); -} - -fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { - let section = include::get(section); - if needed { - out.next_section(); - for line in section.lines() { - if !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - } -} - -fn write_struct(out: &mut OutFile, strct: &Struct) { - for line in strct.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - writeln!(out, "struct {} final {{", strct.ident); - for field in &strct.fields { - write!(out, " "); - write_type_space(out, &field.ty); - writeln!(out, "{};", field.ident); - } - writeln!(out, "}};"); -} - -fn write_struct_decl(out: &mut OutFile, ident: &Ident) { - writeln!(out, "struct {};", ident); -} - -fn write_struct_using(out: &mut OutFile, ident: &Ident) { - writeln!(out, "using {} = {};", ident, ident); -} - -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - for line in ety.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); - for method in methods { - write!(out, " "); - let sig = &method.sig; - let local_name = method.ident.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); - writeln!(out, ";"); - } - writeln!(out, "}};"); -} - -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { - let mut has_cxx_throws = false; - for api in apis { - if let Api::CxxFunction(efn) = api { - if efn.throws { - has_cxx_throws = true; - break; - } - } - } - - if has_cxx_throws { - out.next_section(); - writeln!( - out, - "const char *cxxbridge02$exception(const char *, size_t);", - ); - } -} - -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - if efn.throws { - write!(out, "::rust::Str::Repr "); - } else { - write_extern_return_type_space(out, &efn.ret, types); - } - let mangled = mangle::extern_fn(&out.namespace, efn); - write!(out, "{}(", mangled); - if let Some(receiver) = &efn.receiver { - if receiver.mutability.is_none() { - write!(out, "const "); - } - write!(out, "{} &self", receiver.ty); - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 || efn.receiver.is_some() { - write!(out, ", "); - } - if arg.ty == RustString { - write!(out, "const "); - } else if let Type::RustVec(_) = arg.ty { - write!(out, "const "); - } - write_extern_arg(out, arg, types); - } - let indirect_return = indirect_return(efn, types); - if indirect_return { - if !efn.args.is_empty() { - write!(out, ", "); - } - write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); - write!(out, "*return$"); - } - writeln!(out, ") noexcept {{"); - write!(out, " "); - write_return_type(out, &efn.ret); - match &efn.receiver { - None => write!(out, "(*{}$)(", efn.ident), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type(out, &arg.ty); - } - write!(out, ")"); - if let Some(receiver) = &efn.receiver { - if receiver.mutability.is_none() { - write!(out, " const"); - } - } - write!(out, " = "); - match &efn.receiver { - None => write!(out, "{}", efn.ident), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), - } - writeln!(out, ";"); - write!(out, " "); - if efn.throws { - writeln!(out, "::rust::Str::Repr throw$;"); - writeln!(out, " ::rust::behavior::trycatch("); - writeln!(out, " [&] {{"); - write!(out, " "); - } - if indirect_return { - write!(out, "new (return$) "); - write_indirect_return_type(out, efn.ret.as_ref().unwrap()); - write!(out, "("); - } else if efn.ret.is_some() { - write!(out, "return "); - } - match &efn.ret { - Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), - Some(Type::SliceRefU8(_)) if !indirect_return => { - write!(out, "::rust::Slice::Repr(") - } - _ => {} - } - match &efn.receiver { - None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self.*{}$)(", efn.ident), - } - for (i, arg) in efn.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - if let Type::RustBox(_) = &arg.ty { - write_type(out, &arg.ty); - write!(out, "::from_raw({})", arg.ident); - } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty); - write!(out, "({})", arg.ident); - } else if arg.ty == RustString { - write!( - out, - "::rust::String(::rust::unsafe_bitcopy, *{})", - arg.ident, - ); - } else if let Type::RustVec(_) = arg.ty { - write_type(out, &arg.ty); - write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); - } else if types.needs_indirect_abi(&arg.ty) { - out.include.utility = true; - write!(out, "::std::move(*{})", arg.ident); - } else { - write!(out, "{}", arg.ident); - } - } - write!(out, ")"); - match &efn.ret { - Some(Type::RustBox(_)) => write!(out, ".into_raw()"), - Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::CxxVector(_)) => write!( - out, - " /* Use RVO to convert to r-value and move construct */" - ), - Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), - _ => {} - } - if indirect_return { - write!(out, ")"); - } - writeln!(out, ";"); - if efn.throws { - out.include.cstring = true; - writeln!(out, " throw$.ptr = nullptr;"); - writeln!(out, " }},"); - writeln!(out, " [&](const char *catch$) noexcept {{"); - writeln!(out, " throw$.len = ::std::strlen(catch$);"); - writeln!( - out, - " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", - ); - writeln!(out, " }});"); - writeln!(out, " return throw$;"); - } - writeln!(out, "}}"); - for arg in &efn.args { - if let Type::Fn(f) = &arg.ty { - let var = &arg.ident; - write_function_pointer_trampoline(out, efn, var, f, types); - } - } -} - -fn write_function_pointer_trampoline( - out: &mut OutFile, - efn: &ExternFn, - var: &Ident, - f: &Signature, - types: &Types, -) { - out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); - let indirect_call = true; - write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); - - out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); - write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); -} - -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { - let link_name = mangle::extern_fn(&out.namespace, efn); - let indirect_call = false; - write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); -} - -fn write_rust_function_decl_impl( - out: &mut OutFile, - link_name: &Symbol, - sig: &Signature, - types: &Types, - indirect_call: bool, -) { - if sig.throws { - write!(out, "::rust::Str::Repr "); - } else { - write_extern_return_type_space(out, &sig.ret, types); - } - write!(out, "{}(", link_name); - let mut needs_comma = false; - if let Some(receiver) = &sig.receiver { - if receiver.mutability.is_none() { - write!(out, "const "); - } - write!(out, "{} &self", receiver.ty); - needs_comma = true; - } - for arg in &sig.args { - if needs_comma { - write!(out, ", "); - } - write_extern_arg(out, arg, types); - needs_comma = true; - } - if indirect_return(sig, types) { - if needs_comma { - write!(out, ", "); - } - write_return_type(out, &sig.ret); - write!(out, "*return$"); - needs_comma = true; - } - if indirect_call { - if needs_comma { - write!(out, ", "); - } - write!(out, "void *"); - } - writeln!(out, ") noexcept;"); -} - -fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { - for line in efn.doc.to_string().lines() { - writeln!(out, "//{}", line); - } - let local_name = match &efn.sig.receiver { - None => efn.ident.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), - }; - let invoke = mangle::extern_fn(&out.namespace, efn); - let indirect_call = false; - write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); -} - -fn write_rust_function_shim_decl( - out: &mut OutFile, - local_name: &str, - sig: &Signature, - indirect_call: bool, -) { - write_return_type(out, &sig.ret); - write!(out, "{}(", local_name); - for (i, arg) in sig.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type_space(out, &arg.ty); - write!(out, "{}", arg.ident); - } - if indirect_call { - if !sig.args.is_empty() { - write!(out, ", "); - } - write!(out, "void *extern$"); - } - write!(out, ")"); - if let Some(receiver) = &sig.receiver { - if receiver.mutability.is_none() { - write!(out, " const"); - } - } - if !sig.throws { - write!(out, " noexcept"); - } -} - -fn write_rust_function_shim_impl( - out: &mut OutFile, - local_name: &str, - sig: &Signature, - types: &Types, - invoke: &Symbol, - indirect_call: bool, -) { - if out.header && sig.receiver.is_some() { - // We've already defined this inside the struct. - return; - } - write_rust_function_shim_decl(out, local_name, sig, indirect_call); - if out.header { - writeln!(out, ";"); - return; - } - writeln!(out, " {{"); - for arg in &sig.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { - out.include.utility = true; - write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty); - writeln!(out, "> {}$(::std::move({0}));", arg.ident); - } - } - write!(out, " "); - let indirect_return = indirect_return(sig, types); - if indirect_return { - write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap()); - writeln!(out, "> return$;"); - write!(out, " "); - } else if let Some(ret) = &sig.ret { - write!(out, "return "); - match ret { - Type::RustBox(_) => { - write_type(out, ret); - write!(out, "::from_raw("); - } - Type::UniquePtr(_) => { - write_type(out, ret); - write!(out, "("); - } - Type::Ref(_) => write!(out, "*"), - _ => {} - } - } - if sig.throws { - write!(out, "::rust::Str::Repr error$ = "); - } - write!(out, "{}(", invoke); - if sig.receiver.is_some() { - write!(out, "*this"); - } - for (i, arg) in sig.args.iter().enumerate() { - if i > 0 || sig.receiver.is_some() { - write!(out, ", "); - } - match &arg.ty { - Type::Str(_) => write!(out, "::rust::Str::Repr("), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), - ty if types.needs_indirect_abi(ty) => write!(out, "&"), - _ => {} - } - write!(out, "{}", arg.ident); - match &arg.ty { - Type::RustBox(_) => write!(out, ".into_raw()"), - Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), - ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), - _ => {} - } - } - if indirect_return { - if !sig.args.is_empty() { - write!(out, ", "); - } - write!(out, "&return$.value"); - } - if indirect_call { - if !sig.args.is_empty() || indirect_return { - write!(out, ", "); - } - write!(out, "extern$"); - } - write!(out, ")"); - if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { - write!(out, ")"); - } - } - writeln!(out, ";"); - if sig.throws { - writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::Error(error$);"); - writeln!(out, " }}"); - } - if indirect_return { - out.include.utility = true; - writeln!(out, " return ::std::move(return$.value);"); - } - writeln!(out, "}}"); -} - -fn write_return_type(out: &mut OutFile, ty: &Option) { - match ty { - None => write!(out, "void "), - Some(ty) => write_type_space(out, ty), - } -} - -fn indirect_return(sig: &Signature, types: &Types) -> bool { - sig.ret - .as_ref() - .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) -} - -fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::RustBox(ty) | Type::UniquePtr(ty) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Type::Ref(ty) => { - if ty.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &ty.inner); - write!(out, " *"); - } - Type::Str(_) => write!(out, "::rust::Str::Repr"), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), - _ => write_type(out, ty), - } -} - -fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { - write_indirect_return_type(out, ty); - match ty { - Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} - Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), - _ => write_space_after_type(out, ty), - } -} - -fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { - match ty { - Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Some(Type::Ref(ty)) => { - if ty.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &ty.inner); - write!(out, " *"); - } - Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), - Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), - Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), - _ => write_return_type(out, ty), - } -} - -fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { - match &arg.ty { - Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { - write_type_space(out, &ty.inner); - write!(out, "*"); - } - Type::Str(_) => write!(out, "::rust::Str::Repr "), - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), - _ => write_type_space(out, &arg.ty), - } - if types.needs_indirect_abi(&arg.ty) { - write!(out, "*"); - } - write!(out, "{}", arg.ident); -} - -fn write_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), - None => write!(out, "{}", ident), - }, - Type::RustBox(ty) => { - write!(out, "::rust::Box<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::RustVec(ty) => { - write!(out, "::rust::Vec<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::UniquePtr(ptr) => { - write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); - write!(out, ">"); - } - Type::CxxVector(ty) => { - write!(out, "::std::vector<"); - write_type(out, &ty.inner); - write!(out, ">"); - } - Type::Ref(r) => { - if r.mutability.is_none() { - write!(out, "const "); - } - write_type(out, &r.inner); - write!(out, " &"); - } - Type::Slice(_) => { - // For now, only U8 slices are supported, which are covered separately below - unreachable!() - } - Type::Str(_) => { - write!(out, "::rust::Str"); - } - Type::SliceRefU8(_) => { - write!(out, "::rust::Slice"); - } - Type::Fn(f) => { - write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); - match &f.ret { - Some(ret) => write_type(out, ret), - None => write!(out, "void"), - } - write!(out, "("); - for (i, arg) in f.args.iter().enumerate() { - if i > 0 { - write!(out, ", "); - } - write_type(out, &arg.ty); - } - write!(out, ")>"); - } - Type::Void(_) => unreachable!(), - } -} - -fn write_type_space(out: &mut OutFile, ty: &Type) { - write_type(out, ty); - write_space_after_type(out, ty); -} - -fn write_space_after_type(out: &mut OutFile, ty: &Type) { - match ty { - Type::Ident(_) - | Type::RustBox(_) - | Type::UniquePtr(_) - | Type::Str(_) - | Type::CxxVector(_) - | Type::RustVec(_) - | Type::SliceRefU8(_) - | Type::Fn(_) => write!(out, " "), - Type::Ref(_) => {} - Type::Void(_) | Type::Slice(_) => unreachable!(), - } -} - -// Only called for legal referent types of unique_ptr and element types of -// std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += name; - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), - _ => unreachable!(), - } -} - -// Only called for legal referent types of unique_ptr and element types of -// std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { - match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), - _ => unreachable!(), - } -} - -fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() - } - - out.begin_block("extern \"C\""); - for ty in types { - if let Type::RustBox(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - out.next_section(); - write_rust_box_extern(out, inner); - } - } else if let Type::RustVec(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - out.next_section(); - write_rust_vec_extern(out, inner); - } - } - } else if let Type::UniquePtr(ptr) = ty { - if let Type::Ident(inner) = &ptr.inner { - if allow_unique_ptr(inner) { - out.next_section(); - write_unique_ptr(out, inner, types); - } - } - } else if let Type::CxxVector(ptr) = ty { - if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() { - out.next_section(); - write_cxx_vector(out, ty, inner, types); - } - } - } - } - out.end_block("extern \"C\""); - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); - for ty in types { - if let Type::RustBox(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, inner); - } - } else if let Type::RustVec(ty) = ty { - if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - write_rust_vec_impl(out, inner); - } - } - } - } - out.end_block("namespace cxxbridge02"); - out.end_block("namespace rust"); -} - -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); - - writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); - writeln!( - out, - "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); -} - -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); - writeln!( - out, - "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", - instance, inner, - ); - writeln!( - out, - "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", - inner, instance, - ); - writeln!( - out, - "size_t cxxbridge02$rust_vec${}$stride() noexcept;", - instance, - ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); -} - -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += name; - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); - - writeln!(out, "template <>"); - writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$drop(this);", instance); - writeln!(out, "}}"); -} - -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "template <>"); - writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); - writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); - writeln!( - out, - " return cxxbridge02$rust_vec${}$drop(this);", - instance, - ); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); - writeln!( - out, - " return cxxbridge02$rust_vec${}$data(this);", - instance, - ); - writeln!(out, "}}"); - - writeln!(out, "template <>"); - writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); - writeln!(out, "}}"); -} - -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { - let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); - - writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); - - write_unique_ptr_common(out, &ty, types); - - writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); -} - -// Shared by UniquePtr and UniquePtr>. -fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { - out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); - - let can_construct_from_value = match ty { - Type::Ident(ident) => types.structs.contains_key(ident), - _ => false, - }; - - writeln!( - out, - "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", - inner, - ); - writeln!( - out, - "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", - inner, - ); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, - ); - writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); - writeln!(out, "}}"); - if can_construct_from_value { - writeln!( - out, - "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", - instance, inner, inner, - ); - writeln!( - out, - " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));", - inner, inner, - ); - writeln!(out, "}}"); - } - writeln!( - out, - "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", - instance, inner, inner, - ); - writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); - writeln!(out, "}}"); - writeln!( - out, - "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return ptr.get();"); - writeln!(out, "}}"); - writeln!( - out, - "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return ptr.release();"); - writeln!(out, "}}"); - writeln!( - out, - "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", - instance, inner, - ); - writeln!(out, " ptr->~unique_ptr();"); - writeln!(out, "}}"); -} - -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { - let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); - - writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); - writeln!( - out, - "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", - instance, inner, - ); - writeln!(out, " return s.size();"); - writeln!(out, "}}"); - writeln!( - out, - "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", - inner, instance, inner, - ); - writeln!(out, " return s[pos];"); - writeln!(out, "}}"); - - write_unique_ptr_common(out, vector_ty, types); - - writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); -} diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 82401ee..c7c48ec 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "Implementation detail of the `cxx` crate." repository = "https://github.com/dtolnay/cxx" +exclude = ["README.md"] [lib] proc-macro = true diff --git a/macro/README.md b/macro/README.md new file mode 100644 index 0000000..b9c1779 --- /dev/null +++ b/macro/README.md @@ -0,0 +1,3 @@ +This directory contains CXX's Rust code generator, which is a procedural macro. +Users won't depend on this crate directly. Instead they'll invoke its macro +through the reexport in the main `cxx` crate. diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 740ab94..0000000 --- a/src/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::error::Error as StdError; -use std::fmt::{self, Display}; -use std::io; - -pub(super) type Result = std::result::Result; - -#[derive(Debug)] -pub(super) enum Error { - MissingOutDir, - TargetDir, - Io(io::Error), -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), - Error::TargetDir => write!(f, "failed to locate target dir"), - Error::Io(err) => err.fmt(f), - } - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Error::Io(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) - } -} diff --git a/src/gen b/src/gen deleted file mode 120000 index 334e0fb..0000000 --- a/src/gen +++ /dev/null @@ -1 +0,0 @@ -../gen \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index cff1e88..7e8e9ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,8 +224,7 @@ //! // build.rs //! //! fn main() { -//! cxx::Build::new() -//! .bridge("src/main.rs") // returns a cc::Build +//! cxx_build::bridge("src/main.rs") // returns a cc::Build //! .file("../demo-cxx/demo.cc") //! .flag("-std=c++11") //! .compile("cxxbridge-demo"); @@ -363,18 +362,14 @@ mod concat; mod cxx_string; mod cxx_vector; -mod error; mod exception; mod function; -mod gen; mod opaque; -mod paths; mod result; mod rust_sliceu8; mod rust_str; mod rust_string; mod rust_vec; -mod syntax; mod unique_ptr; mod unwind; @@ -398,107 +393,3 @@ pub mod private { pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } - -use crate::error::Result; -use crate::gen::Opt; -use anyhow::anyhow; -use std::fs; -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -/// The CXX code generator for constructing and compiling C++ code. -/// -/// This is intended to be used from Cargo build scripts to execute CXX's -/// C++ code generator, set up any additional compiler flags depending on -/// the use case, and make the C++ compiler invocation. -/// -///
-/// -/// # Example -/// -/// Example of a canonical Cargo build script that builds a CXX bridge: -/// -/// ```no_run -/// // build.rs -/// -/// fn main() { -/// cxx::Build::new() -/// .bridge("src/main.rs") -/// .file("../demo-cxx/demo.cc") -/// .flag("-std=c++11") -/// .compile("cxxbridge-demo"); -/// -/// println!("cargo:rerun-if-changed=src/main.rs"); -/// println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -/// println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); -/// } -/// ``` -/// -/// A runnable working setup with this build script is shown in the -/// *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. -/// -/// [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -/// -///
-/// -/// # Alternatives -/// -/// For use in non-Cargo builds like Bazel or Buck, CXX provides an -/// alternate way of invoking the C++ code generator as a standalone command -/// line tool. The tool is packaged as the `cxxbridge-cmd` crate. -/// -/// ```bash -/// $ cargo install cxxbridge-cmd # or build it from the repo -/// -/// $ cxxbridge src/main.rs --header > path/to/mybridge.h -/// $ cxxbridge src/main.rs > path/to/mybridge.cc -/// ``` -#[must_use] -pub struct Build { - _private: (), -} - -impl Build { - /// Begin with a [`cc::Build`] in its default configuration. - pub fn new() -> Self { - Build { _private: () } - } - - /// This returns a [`cc::Build`] on which you should continue to set up - /// any additional source files or compiler flags, and lastly call its - /// [`compile`] method to execute the C++ build. - /// - /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile - #[must_use] - pub fn bridge(&self, rust_source_file: impl AsRef) -> cc::Build { - match try_generate_bridge(rust_source_file.as_ref()) { - Ok(build) => build, - Err(err) => { - let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); - process::exit(1); - } - } - } -} - -fn try_generate_bridge(rust_source_file: &Path) -> Result { - let header = gen::do_generate_header(rust_source_file, Opt::default()); - let header_path = paths::out_with_extension(rust_source_file, ".h")?; - fs::create_dir_all(header_path.parent().unwrap())?; - fs::write(&header_path, header)?; - paths::symlink_header(&header_path, rust_source_file); - - let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); - let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; - fs::write(&bridge_path, bridge)?; - let mut build = paths::cc_build(); - build.file(&bridge_path); - - let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); - let _ = fs::create_dir_all(cxx_h.parent().unwrap()); - let _ = fs::remove_file(cxx_h); - let _ = fs::write(cxx_h, gen::include::HEADER); - - Ok(build) -} diff --git a/src/paths.rs b/src/paths.rs deleted file mode 100644 index ca183d9..0000000 --- a/src/paths.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::error::{Error, Result}; -use std::env; -use std::fs; -use std::path::{Path, PathBuf}; - -fn out_dir() -> Result { - env::var_os("OUT_DIR") - .map(PathBuf::from) - .ok_or(Error::MissingOutDir) -} - -pub(crate) fn cc_build() -> cc::Build { - try_cc_build().unwrap_or_default() -} - -fn try_cc_build() -> Result { - let mut build = cc::Build::new(); - build.include(include_dir()?); - build.include(target_dir()?.parent().unwrap()); - Ok(build) -} - -// Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(path: &Path, original: &Path) { - let _ = try_symlink_header(path, original); -} - -fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(original)?; - let ref dst = include_dir()?.join(suffix); - - fs::create_dir_all(dst.parent().unwrap())?; - let _ = fs::remove_file(dst); - symlink_or_copy(path, dst)?; - - let mut file_name = dst.file_name().unwrap().to_os_string(); - file_name.push(".h"); - let ref dst2 = dst.with_file_name(file_name); - symlink_or_copy(path, dst2)?; - - Ok(()) -} - -fn relative_to_parent_of_target_dir(original: &Path) -> Result { - let target_dir = target_dir()?; - let mut outer = target_dir.parent().unwrap(); - let original = canonicalize(original)?; - loop { - if let Ok(suffix) = original.strip_prefix(outer) { - return Ok(suffix.to_owned()); - } - match outer.parent() { - Some(parent) => outer = parent, - None => return Ok(original.components().skip(1).collect()), - } - } -} - -pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { - let mut file_name = path.file_name().unwrap().to_owned(); - file_name.push(ext); - - let out_dir = out_dir()?; - let rel = relative_to_parent_of_target_dir(path)?; - Ok(out_dir.join(rel).with_file_name(file_name)) -} - -pub(crate) fn include_dir() -> Result { - let target_dir = target_dir()?; - Ok(target_dir.join("cxxbridge")) -} - -fn target_dir() -> Result { - let mut dir = out_dir().and_then(canonicalize)?; - loop { - if dir.ends_with("target") { - return Ok(dir); - } - if !dir.pop() { - return Err(Error::TargetDir); - } - } -} - -#[cfg(not(windows))] -fn canonicalize(path: impl AsRef) -> Result { - Ok(fs::canonicalize(path)?) -} - -#[cfg(windows)] -fn canonicalize(path: impl AsRef) -> Result { - // Real fs::canonicalize on Windows produces UNC paths which cl.exe is - // unable to handle in includes. Use a poor approximation instead. - // https://github.com/rust-lang/rust/issues/42869 - // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(env::current_dir()?.join(path)) -} - -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_or_copy; - -#[cfg(windows)] -fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { - use std::os::windows::fs::symlink_file; - - // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they - // require Developer Mode. If it fails, fall back to copying the file. - if symlink_file(src, dst).is_err() { - fs::copy(src, dst)?; - } - Ok(()) -} - -#[cfg(not(any(unix, windows)))] -use std::fs::copy as symlink_or_copy; diff --git a/src/syntax b/src/syntax deleted file mode 120000 index f400712..0000000 --- a/src/syntax +++ /dev/null @@ -1 +0,0 @@ -../syntax \ No newline at end of file diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index c84df61..c2d8227 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -11,4 +11,4 @@ path = "lib.rs" cxx = { path = "../.." } [build-dependencies] -cxx = { path = "../.." } +cxx-build = { path = "../../gen/build" } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 5c1bec3..b970362 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,8 +3,7 @@ fn main() { return; } - cxx::Build::new() - .bridge("lib.rs") + cxx_build::bridge("lib.rs") .file("tests.cc") .flag("-std=c++11") .compile("cxx-test-suite"); diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 0bd470b..f4068d9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -67,17 +67,25 @@ dependencies = [ name = "cxx" version = "0.2.12" dependencies = [ - "anyhow", "cc", - "codespan-reporting", + "cxx-build", "cxx-test-suite", "cxxbridge-macro", "link-cplusplus", + "rustversion", + "trybuild", +] + +[[package]] +name = "cxx-build" +version = "0.2.12" +dependencies = [ + "anyhow", + "cc", + "codespan-reporting", "proc-macro2", "quote", - "rustversion", "syn", - "trybuild", ] [[package]] @@ -85,6 +93,7 @@ name = "cxx-test-suite" version = "0.0.0" dependencies = [ "cxx", + "cxx-build", ] [[package]] @@ -104,6 +113,7 @@ name = "cxxbridge-demo" version = "0.0.0" dependencies = [ "cxx", + "cxx-build", ] [[package]] From 6960162922c22b26404793810d89edb898628b9a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:49:50 +0000 Subject: [PATCH 486/2232] Bump namespace to cxxbridge03 --- diff --git a/Cargo.toml b/Cargo.toml index ea97712..ad40cad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "cxx" version = "0.2.12" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" -links = "cxxbridge02" +links = "cxxbridge03" license = "MIT OR Apache-2.0" description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" diff --git a/build.rs b/build.rs index 5173658..1f3b6eb 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,7 @@ fn main() { cc::Build::new() .file("src/cxx.cc") .flag("-std=c++11") - .compile("cxxbridge02"); + .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); } diff --git a/gen/src/write.rs b/gen/src/write.rs index b39d978..8869849 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -218,7 +218,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); + out.begin_block("inline namespace cxxbridge03"); if needs_rust_string || needs_rust_str @@ -241,15 +241,15 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "struct unsafe_bitcopy_t;"); } - write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING"); - write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR"); - write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE"); - write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX"); - write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC"); - write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN"); - write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE"); - write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY"); + write_header_section(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); + write_header_section(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); + write_header_section(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); + write_header_section(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); + write_header_section(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); + write_header_section(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); + write_header_section(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); + write_header_section(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); + write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); @@ -275,7 +275,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } - out.end_block("namespace cxxbridge02"); + out.end_block("namespace cxxbridge03"); if needs_trycatch { out.begin_block("namespace behavior"); @@ -368,7 +368,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { out.next_section(); writeln!( out, - "const char *cxxbridge02$exception(const char *, size_t);", + "const char *cxxbridge03$exception(const char *, size_t);", ); } } @@ -506,7 +506,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);", + " throw$.ptr = cxxbridge03$exception(catch$, throw$.len);", ); writeln!(out, " }});"); writeln!(out, " return throw$;"); @@ -977,7 +977,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("extern \"C\""); out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge02"); + out.begin_block("inline namespace cxxbridge03"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -991,7 +991,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block("namespace cxxbridge02"); + out.end_block("namespace cxxbridge03"); out.end_block("namespace rust"); } @@ -1004,19 +1004,19 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE03_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE03_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge03$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge03$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance); + writeln!(out, "#endif // CXXBRIDGE03_RUST_BOX_{}", instance); } fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { @@ -1024,34 +1024,34 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance); - writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE03_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE03_RUST_VEC_{}", instance); writeln!( out, - "void cxxbridge02$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge03$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge03$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + "size_t cxxbridge03$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge02$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + "const {} *cxxbridge03$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", inner, instance, ); writeln!( out, - "size_t cxxbridge02$rust_vec${}$stride() noexcept;", + "size_t cxxbridge03$rust_vec${}$stride() noexcept;", instance, ); - writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance); + writeln!(out, "#endif // CXXBRIDGE03_RUST_VEC_{}", instance); } fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { @@ -1065,12 +1065,12 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$uninit(this);", instance); + writeln!(out, " cxxbridge03$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " cxxbridge02$box${}$drop(this);", instance); + writeln!(out, " cxxbridge03$box${}$drop(this);", instance); writeln!(out, "}}"); } @@ -1081,35 +1081,35 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); - writeln!(out, " cxxbridge02$rust_vec${}$new(this);", instance); + writeln!(out, " cxxbridge03$rust_vec${}$new(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!( out, - " return cxxbridge02$rust_vec${}$drop(this);", + " return cxxbridge03$rust_vec${}$drop(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance); + writeln!(out, " return cxxbridge03$rust_vec${}$len(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); writeln!( out, - " return cxxbridge02$rust_vec${}$data(this);", + " return cxxbridge03$rust_vec${}$data(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); - writeln!(out, " return cxxbridge02$rust_vec${}$stride();", instance); + writeln!(out, " return cxxbridge03$rust_vec${}$stride();", instance); writeln!(out, "}}"); } @@ -1117,12 +1117,12 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { let ty = Type::Ident(ident.clone()); let instance = to_mangled(&out.namespace, &ty); - writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE03_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE03_UNIQUE_PTR_{}", instance); write_unique_ptr_common(out, &ty, types); - writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE03_UNIQUE_PTR_{}", instance); } // Shared by UniquePtr and UniquePtr>. @@ -1148,7 +1148,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!( out, - "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge03$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); @@ -1156,7 +1156,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { if can_construct_from_value { writeln!( out, - "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge03$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( @@ -1168,28 +1168,28 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { } writeln!( out, - "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge03$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge03$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge03$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge03$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); @@ -1201,18 +1201,18 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE02_VECTOR_{}", instance); - writeln!(out, "#define CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE03_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE03_VECTOR_{}", instance); writeln!( out, - "size_t cxxbridge02$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", + "size_t cxxbridge03$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); writeln!(out, "}}"); writeln!( out, - "const {} &cxxbridge02$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + "const {} &cxxbridge03$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); writeln!(out, " return s[pos];"); @@ -1220,5 +1220,5 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: write_unique_ptr_common(out, vector_ty, types); - writeln!(out, "#endif // CXXBRIDGE02_VECTOR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE03_VECTOR_{}", instance); } diff --git a/include/cxx.h b/include/cxx.h index 1e33618..7762719 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -13,18 +13,18 @@ #endif namespace rust { -inline namespace cxxbridge02 { +inline namespace cxxbridge03 { -#ifndef CXXBRIDGE02_RUST_BITCOPY -#define CXXBRIDGE02_RUST_BITCOPY +#ifndef CXXBRIDGE03_RUST_BITCOPY +#define CXXBRIDGE03_RUST_BITCOPY struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; }; constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE02_RUST_BITCOPY +#endif // CXXBRIDGE03_RUST_BITCOPY -#ifndef CXXBRIDGE02_RUST_STRING -#define CXXBRIDGE02_RUST_STRING +#ifndef CXXBRIDGE03_RUST_STRING +#define CXXBRIDGE03_RUST_STRING class String final { public: String() noexcept; @@ -52,10 +52,10 @@ private: // Size and alignment statically verified by rust_string.rs. std::array repr; }; -#endif // CXXBRIDGE02_RUST_STRING +#endif // CXXBRIDGE03_RUST_STRING -#ifndef CXXBRIDGE02_RUST_STR -#define CXXBRIDGE02_RUST_STR +#ifndef CXXBRIDGE03_RUST_STR +#define CXXBRIDGE03_RUST_STR class Str final { public: Str() noexcept; @@ -88,10 +88,10 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE02_RUST_STR +#endif // CXXBRIDGE03_RUST_STR -#ifndef CXXBRIDGE02_RUST_SLICE -#define CXXBRIDGE02_RUST_SLICE +#ifndef CXXBRIDGE03_RUST_SLICE +#define CXXBRIDGE03_RUST_SLICE template class Slice final { public: @@ -124,10 +124,10 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE02_RUST_SLICE +#endif // CXXBRIDGE03_RUST_SLICE -#ifndef CXXBRIDGE02_RUST_BOX -#define CXXBRIDGE02_RUST_BOX +#ifndef CXXBRIDGE03_RUST_BOX +#define CXXBRIDGE03_RUST_BOX template class Box final { public: @@ -204,10 +204,10 @@ private: void drop() noexcept; T *ptr; }; -#endif // CXXBRIDGE02_RUST_BOX +#endif // CXXBRIDGE03_RUST_BOX -#ifndef CXXBRIDGE02_RUST_VEC -#define CXXBRIDGE02_RUST_VEC +#ifndef CXXBRIDGE03_RUST_VEC +#define CXXBRIDGE03_RUST_VEC template class Vec final { public: @@ -289,10 +289,10 @@ private: // Size and alignment statically verified by rust_vec.rs. std::array repr; }; -#endif // CXXBRIDGE02_RUST_VEC +#endif // CXXBRIDGE03_RUST_VEC -#ifndef CXXBRIDGE02_RUST_FN -#define CXXBRIDGE02_RUST_FN +#ifndef CXXBRIDGE03_RUST_FN +#define CXXBRIDGE03_RUST_FN template class Fn; @@ -309,10 +309,10 @@ private: template using TryFn = Fn; -#endif // CXXBRIDGE02_RUST_FN +#endif // CXXBRIDGE03_RUST_FN -#ifndef CXXBRIDGE02_RUST_ERROR -#define CXXBRIDGE02_RUST_ERROR +#ifndef CXXBRIDGE03_RUST_ERROR +#define CXXBRIDGE03_RUST_ERROR class Error final : std::exception { public: Error(const Error &); @@ -324,16 +324,16 @@ public: private: Str::Repr msg; }; -#endif // CXXBRIDGE02_RUST_ERROR +#endif // CXXBRIDGE03_RUST_ERROR -#ifndef CXXBRIDGE02_RUST_ISIZE -#define CXXBRIDGE02_RUST_ISIZE +#ifndef CXXBRIDGE03_RUST_ISIZE +#define CXXBRIDGE03_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif -#endif // CXXBRIDGE02_RUST_ISIZE +#endif // CXXBRIDGE03_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -359,5 +359,5 @@ Fn Fn::operator*() const noexcept { return *this; } -} // namespace cxxbridge02 +} // namespace cxxbridge03 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 27d75fe..15a091b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -517,7 +517,7 @@ fn expand_rust_function_shim_impl( } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -546,7 +546,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge02$rust_vec${}{}$", namespace, elem); + let link_prefix = format!("cxxbridge03$rust_vec${}{}$", namespace, elem); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -592,7 +592,7 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { let name = ident.to_string(); - let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident); + let prefix = format!("cxxbridge03$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -665,10 +665,10 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { let name = elem.to_string(); - let prefix = format!("cxxbridge02$std$vector${}{}$", namespace, elem); + let prefix = format!("cxxbridge03$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge02$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!("cxxbridge03$unique_ptr$std$vector${}{}$", namespace, elem); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/src/cxx.cc b/src/cxx.cc index 8409772..5f7ac6a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -17,72 +17,72 @@ static void panic [[noreturn]] (const char *msg) { } extern "C" { -const char *cxxbridge02$cxx_string$data(const std::string &s) noexcept { +const char *cxxbridge03$cxx_string$data(const std::string &s) noexcept { return s.data(); } -size_t cxxbridge02$cxx_string$length(const std::string &s) noexcept { +size_t cxxbridge03$cxx_string$length(const std::string &s) noexcept { return s.length(); } // rust::String -void cxxbridge02$string$new(rust::String *self) noexcept; -void cxxbridge02$string$clone(rust::String *self, +void cxxbridge03$string$new(rust::String *self) noexcept; +void cxxbridge03$string$clone(rust::String *self, const rust::String &other) noexcept; -bool cxxbridge02$string$from(rust::String *self, const char *ptr, +bool cxxbridge03$string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge02$string$drop(rust::String *self) noexcept; -const char *cxxbridge02$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge02$string$len(const rust::String *self) noexcept; +void cxxbridge03$string$drop(rust::String *self) noexcept; +const char *cxxbridge03$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge03$string$len(const rust::String *self) noexcept; // rust::Str -bool cxxbridge02$str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge03$str$valid(const char *ptr, size_t len) noexcept; } // extern "C" namespace rust { -inline namespace cxxbridge02 { +inline namespace cxxbridge03 { -String::String() noexcept { cxxbridge02$string$new(this); } +String::String() noexcept { cxxbridge03$string$new(this); } String::String(const String &other) noexcept { - cxxbridge02$string$clone(this, other); + cxxbridge03$string$clone(this, other); } String::String(String &&other) noexcept { this->repr = other.repr; - cxxbridge02$string$new(&other); + cxxbridge03$string$new(&other); } -String::~String() noexcept { cxxbridge02$string$drop(this); } +String::~String() noexcept { cxxbridge03$string$drop(this); } String::String(const std::string &s) { auto ptr = s.data(); auto len = s.length(); - if (!cxxbridge02$string$from(this, ptr, len)) { + if (!cxxbridge03$string$from(this, ptr, len)) { panic("data for rust::String is not utf-8"); } } String::String(const char *s) { auto len = std::strlen(s); - if (!cxxbridge02$string$from(this, s, len)) { + if (!cxxbridge03$string$from(this, s, len)) { panic("data for rust::String is not utf-8"); } } String &String::operator=(const String &other) noexcept { if (this != &other) { - cxxbridge02$string$drop(this); - cxxbridge02$string$clone(this, other); + cxxbridge03$string$drop(this); + cxxbridge03$string$clone(this, other); } return *this; } String &String::operator=(String &&other) noexcept { if (this != &other) { - cxxbridge02$string$drop(this); + cxxbridge03$string$drop(this); this->repr = other.repr; - cxxbridge02$string$new(&other); + cxxbridge03$string$new(&other); } return *this; } @@ -92,12 +92,12 @@ String::operator std::string() const { } const char *String::data() const noexcept { - return cxxbridge02$string$ptr(this); + return cxxbridge03$string$ptr(this); } -size_t String::size() const noexcept { return cxxbridge02$string$len(this); } +size_t String::size() const noexcept { return cxxbridge03$string$len(this); } -size_t String::length() const noexcept { return cxxbridge02$string$len(this); } +size_t String::length() const noexcept { return cxxbridge03$string$len(this); } String::String(unsafe_bitcopy_t, const String &bits) noexcept : repr(bits.repr) {} @@ -112,13 +112,13 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const Str &) noexcept = default; Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } } Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { - if (!cxxbridge02$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } } @@ -148,7 +148,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { } extern "C" { -const char *cxxbridge02$error(const char *ptr, size_t len) { +const char *cxxbridge03$error(const char *ptr, size_t len) { char *copy = new char[len]; strncpy(copy, ptr, len); return copy; @@ -158,7 +158,7 @@ const char *cxxbridge02$error(const char *ptr, size_t len) { Error::Error(Str::Repr msg) noexcept : msg(msg) {} Error::Error(const Error &other) { - this->msg.ptr = cxxbridge02$error(other.msg.ptr, other.msg.len); + this->msg.ptr = cxxbridge03$error(other.msg.ptr, other.msg.len); this->msg.len = other.msg.len; } @@ -173,96 +173,96 @@ Error::~Error() noexcept { delete[] this->msg.ptr; } const char *Error::what() const noexcept { return this->msg.ptr; } -} // namespace cxxbridge02 +} // namespace cxxbridge03 } // namespace rust extern "C" { -void cxxbridge02$unique_ptr$std$string$null( +void cxxbridge03$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge02$unique_ptr$std$string$raw(std::unique_ptr *ptr, +void cxxbridge03$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); } -const std::string *cxxbridge02$unique_ptr$std$string$get( +const std::string *cxxbridge03$unique_ptr$std$string$get( const std::unique_ptr &ptr) noexcept { return ptr.get(); } -std::string *cxxbridge02$unique_ptr$std$string$release( +std::string *cxxbridge03$unique_ptr$std$string$release( std::unique_ptr &ptr) noexcept { return ptr.release(); } -void cxxbridge02$unique_ptr$std$string$drop( +void cxxbridge03$unique_ptr$std$string$drop( std::unique_ptr *ptr) noexcept { ptr->~unique_ptr(); } } // extern "C" #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ - size_t cxxbridge02$std$vector$##RUST_TYPE##$size( \ + size_t cxxbridge03$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ } \ - const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked( \ + const CXX_TYPE *cxxbridge03$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ return &s[pos]; \ } \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null( \ + void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ } \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$raw( \ std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ new (ptr) std::unique_ptr>(raw); \ } \ const std::vector \ - *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get( \ + *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$get( \ const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ std::vector \ - *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release( \ + *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$release( \ std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ - void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$drop( \ std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ } #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ - void cxxbridge02$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge03$rust_vec$##RUST_TYPE##$new( \ rust::Vec *ptr) noexcept; \ - void cxxbridge02$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge03$rust_vec$##RUST_TYPE##$drop( \ rust::Vec *ptr) noexcept; \ - size_t cxxbridge02$rust_vec$##RUST_TYPE##$len( \ + size_t cxxbridge03$rust_vec$##RUST_TYPE##$len( \ const rust::Vec *ptr) noexcept; \ - const CXX_TYPE *cxxbridge02$rust_vec$##RUST_TYPE##$data( \ + const CXX_TYPE *cxxbridge03$rust_vec$##RUST_TYPE##$data( \ const rust::Vec *ptr) noexcept; \ - size_t cxxbridge02$rust_vec$##RUST_TYPE##$stride() noexcept; + size_t cxxbridge03$rust_vec$##RUST_TYPE##$stride() noexcept; #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ Vec::Vec() noexcept { \ - cxxbridge02$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge03$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Vec::drop() noexcept { \ - return cxxbridge02$rust_vec$##RUST_TYPE##$drop(this); \ + return cxxbridge03$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ size_t Vec::size() const noexcept { \ - return cxxbridge02$rust_vec$##RUST_TYPE##$len(this); \ + return cxxbridge03$rust_vec$##RUST_TYPE##$len(this); \ } \ template <> \ const CXX_TYPE *Vec::data() const noexcept { \ - return cxxbridge02$rust_vec$##RUST_TYPE##$data(this); \ + return cxxbridge03$rust_vec$##RUST_TYPE##$data(this); \ } \ template <> \ size_t Vec::stride() noexcept { \ - return cxxbridge02$rust_vec$##RUST_TYPE##$stride(); \ + return cxxbridge03$rust_vec$##RUST_TYPE##$stride(); \ } // Usize and isize are the same type as one of the below. @@ -289,7 +289,7 @@ FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) } // extern "C" namespace rust { -inline namespace cxxbridge02 { +inline namespace cxxbridge03 { FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) -} // namespace cxxbridge02 +} // namespace cxxbridge03 } // namespace rust diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 54fad27..ffd0c5c 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -4,9 +4,9 @@ use std::slice; use std::str::{self, Utf8Error}; extern "C" { - #[link_name = "cxxbridge02$cxx_string$data"] + #[link_name = "cxxbridge03$cxx_string$data"] fn string_data(_: &CxxString) -> *const u8; - #[link_name = "cxxbridge02$cxx_string$length"] + #[link_name = "cxxbridge03$cxx_string$length"] fn string_length(_: &CxxString) -> usize; } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 695fcad..0d952f9 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -137,7 +137,7 @@ macro_rules! impl_vector_element_for_primitive { fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$size")] + #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$size")] fn __vector_size(_: &CxxVector<$ty>) -> usize; } } @@ -146,7 +146,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$get_unchecked")] + #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } @@ -155,7 +155,7 @@ macro_rules! impl_vector_element_for_primitive { fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$null")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$null")] fn __unique_ptr_null(this: *mut *mut c_void); } } @@ -166,7 +166,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$raw")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$raw")] fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); } } @@ -177,7 +177,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$get")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$get")] fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; } } @@ -186,7 +186,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$release")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$release")] fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; } } @@ -195,7 +195,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { extern "C" { attr! { - #[link_name = concat!("cxxbridge02$unique_ptr$std$vector$", stringify!($ty), "$drop")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$drop")] fn __unique_ptr_drop(this: *mut *mut c_void); } } diff --git a/src/exception.rs b/src/exception.rs index c436196..52f0cf6 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -21,7 +21,7 @@ impl Exception { } } -#[export_name = "cxxbridge02$exception"] +#[export_name = "cxxbridge03$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { let slice = slice::from_raw_parts(ptr, len); let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); diff --git a/src/result.rs b/src/result.rs index 296efea..0373bd8 100644 --- a/src/result.rs +++ b/src/result.rs @@ -32,7 +32,7 @@ unsafe fn to_c_error(msg: String) -> Result { let len = msg.len(); extern "C" { - #[link_name = "cxxbridge02$error"] + #[link_name = "cxxbridge03$error"] fn error(ptr: *const u8, len: usize) -> *const u8; } diff --git a/src/rust_str.rs b/src/rust_str.rs index 184e2c1..fe11473 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -25,7 +25,7 @@ impl RustStr { } } -#[export_name = "cxxbridge02$str$valid"] +#[export_name = "cxxbridge03$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/rust_string.rs b/src/rust_string.rs index 3fba2f5..6f3e64b 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -30,17 +30,17 @@ impl RustString { } } -#[export_name = "cxxbridge02$string$new"] +#[export_name = "cxxbridge03$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge02$string$clone"] +#[export_name = "cxxbridge03$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge02$string$from"] +#[export_name = "cxxbridge03$string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -56,17 +56,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge02$string$drop"] +#[export_name = "cxxbridge03$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge02$string$ptr"] +#[export_name = "cxxbridge03$string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge02$string$len"] +#[export_name = "cxxbridge03$string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } diff --git a/src/rust_vec.rs b/src/rust_vec.rs index d5de489..c17310d 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -47,31 +47,31 @@ macro_rules! rust_vec_shims_for_primitive { const _: () = { attr! { - #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$new")] + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { ptr::write(this, RustVec::new()); } } attr! { - #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$drop")] + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); } } attr! { - #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$len")] + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { (*this).len() } } attr! { - #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$data")] + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { (*this).as_ptr() } } attr! { - #[export_name = concat!("cxxbridge02$rust_vec$", stringify!($ty), "$stride")] + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")] unsafe extern "C" fn __stride() -> usize { mem::size_of::<$ty>() } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 98b4b7d..34a1f0e 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -174,15 +174,15 @@ pub unsafe trait UniquePtrTarget { } extern "C" { - #[link_name = "cxxbridge02$unique_ptr$std$string$null"] + #[link_name = "cxxbridge03$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge02$unique_ptr$std$string$raw"] + #[link_name = "cxxbridge03$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); - #[link_name = "cxxbridge02$unique_ptr$std$string$get"] + #[link_name = "cxxbridge03$unique_ptr$std$string$get"] fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; - #[link_name = "cxxbridge02$unique_ptr$std$string$release"] + #[link_name = "cxxbridge03$unique_ptr$std$string$release"] fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; - #[link_name = "cxxbridge02$unique_ptr$std$string$drop"] + #[link_name = "cxxbridge03$unique_ptr$std$string$drop"] fn unique_ptr_std_string_drop(this: *mut *mut c_void); } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 1380704..c9392db 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -3,7 +3,7 @@ use crate::syntax::symbol::{self, Symbol}; use crate::syntax::ExternFn; use proc_macro2::Ident; -const CXXBRIDGE: &str = "cxxbridge02"; +const CXXBRIDGE: &str = "cxxbridge03"; macro_rules! join { ($($segment:expr),*) => { diff --git a/syntax/symbol.rs b/syntax/symbol.rs index fa8e587..066d238 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -4,7 +4,7 @@ use quote::ToTokens; use std::fmt::{self, Display, Write}; // A mangled symbol consisting of segments separated by '$'. -// For example: cxxbridge02$string$new +// For example: cxxbridge03$string$new pub struct Symbol(String); impl Display for Symbol { From c1d23a07ff5e18687e2a90e1ee578fe97361237d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:51:55 +0000 Subject: [PATCH 487/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 54a1e85..3500cb0 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -31,7 +31,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.2/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.3/src/**"]), visibility = ["PUBLIC"], deps = [ ":termcolor", diff --git a/third-party/BUILD b/third-party/BUILD index c6ce473..b6605e4 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -36,7 +36,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.2/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.3/src/**"]), visibility = ["//visibility:public"], deps = [ ":termcolor", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f4068d9..b778e5b 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efca5ddfdf45cee2eedd9dadbe7a5fa90a5536af6f580f1814267b2a4d6107f" +checksum = "d5680df8512a0e825b9edc41b619ec88b367644d394b4d862a04b4d6387c65da" dependencies = [ "termcolor", "unicode-width", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" dependencies = [ "itoa", "ryu", From cc9ece5186a3a06becdfac2d42e5c86252d277f7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:57:05 +0000 Subject: [PATCH 488/2232] Document the line that needs to go in build-dependencies --- diff --git a/README.md b/README.md index 59bcdac..78a62a2 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,13 @@ set up any additional source files and compiler flags as normal. [`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html +```toml +# Cargo.toml + +[build-dependencies] +cxx-build = "0.2" +``` + ```rust // build.rs diff --git a/src/lib.rs b/src/lib.rs index 7e8e9ec..f97ab96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,6 +220,13 @@ //! //! [`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html //! +//! ```toml +//! # Cargo.toml +//! +//! [build-dependencies] +//! cxx-build = "0.2" +//! ``` +//! //! ```no_run //! // build.rs //! From 63a4384af71e9920bd7e1bcc59b1d80f2380192c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 01:57:39 +0000 Subject: [PATCH 489/2232] Release 0.3.0 --- diff --git a/Cargo.toml b/Cargo.toml index ad40cad..f5e5b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.2.12" # remember to update html_root_url +version = "0.3.0" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -15,14 +15,14 @@ exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] travis-ci = { repository = "dtolnay/cxx" } [dependencies] -cxxbridge-macro = { version = "=0.2.12", path = "macro" } +cxxbridge-macro = { version = "=0.3.0", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.2.12", path = "gen/build" } +cxx-build = { version = "=0.3.0", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.21", features = ["diff"] } diff --git a/README.md b/README.md index 78a62a2..f1cf00f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ can be 100% safe. ```toml [dependencies] -cxx = "0.2" +cxx = "0.3" ``` *Compiler support: requires rustc 1.42+*
@@ -219,7 +219,7 @@ set up any additional source files and compiler flags as normal. # Cargo.toml [build-dependencies] -cxx-build = "0.2" +cxx-build = "0.3" ``` ```rust @@ -307,11 +307,11 @@ returns of functions. Stringrust::String &strrust::Str &[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet -CxxStringstd::stringcannot be passed by value +CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type -UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type Vec<T>rust::Vec<T>cannot hold opaque C++ type -CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type +CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far Result<T>throw/catchallowed as return type only diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 3552524..869f543 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.2.12" +version = "0.3.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 742d54b..9927d3f 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.2.12" +version = "0.3.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index c7c48ec..51fba71 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.2.12" +version = "0.3.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -20,7 +20,7 @@ quote = "1.0" syn = { version = "1.0", features = ["full"] } [dev-dependencies] -cxx = { version = "0.2", path = ".." } +cxx = { version = "0.3", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/lib.rs b/src/lib.rs index f97ab96..37fc5ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -224,7 +224,7 @@ //! # Cargo.toml //! //! [build-dependencies] -//! cxx-build = "0.2" +//! cxx-build = "0.3" //! ``` //! //! ```no_run @@ -343,7 +343,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.2.12")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.0")] #![deny(improper_ctypes)] #![allow( clippy::cognitive_complexity, diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b778e5b..f4e5903 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.2.12" +version = "0.3.0" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.2.12" +version = "0.3.0" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.2.12" +version = "0.3.0" dependencies = [ "anyhow", "codespan-reporting", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.2.12" +version = "0.3.0" dependencies = [ "cxx", "proc-macro2", From a5ca1164b7e457641a01fcf0911ced8929d1efa9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 05:03:27 +0000 Subject: [PATCH 490/2232] Pin bazel CI to stable 1.43.0 --- diff --git a/WORKSPACE b/WORKSPACE index 501bde0..4269fbc 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,15 +24,13 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_43_beta_linux", + name = "rust_1_43_linux", exec_triple = "x86_64-unknown-linux-gnu", - iso_date = "2020-04-19", - version = "beta", + version = "1.43.0", ) rust_repository_set( - name = "rust_1_43_beta_darwin", + name = "rust_1_43_darwin", exec_triple = "x86_64-apple-darwin", - iso_date = "2020-04-19", - version = "beta", + version = "1.43.0", ) From 74dd379f093a2955d7657dffb7d5acfa9e9aeaaa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 14:45:24 +0000 Subject: [PATCH 491/2232] Format PR 157 with clang-format --- diff --git a/include/cxx.h b/include/cxx.h index 7762719..d870442 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -237,8 +237,8 @@ public: public: using difference_type = ptrdiff_t; using value_type = typename std::add_const::type; - using pointer = typename std::add_pointer< - typename std::add_const::type>::type; + using pointer = + typename std::add_pointer::type>::type; using reference = typename std::add_lvalue_reference< typename std::add_const::type>::type; using iterator_category = std::forward_iterator_tag; From 3c90cd2ef80e799c249436f4679aa3c12d1f2b91 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 14:45:34 +0000 Subject: [PATCH 492/2232] Move Rust symbols required by C++ to a separate Buck target Closes #168. --- diff --git a/BUCK b/BUCK index 2339f71..acf3a4c 100644 --- a/BUCK +++ b/BUCK @@ -1,7 +1,8 @@ rust_library( name = "cxx", - srcs = glob(["src/**"]), + srcs = glob(["src/**"], exclude = ["src/symbols/**"]), visibility = ["PUBLIC"], + rustc_flags = ["--cfg", "no_export_symbols"], deps = [ ":core", ":macro", @@ -33,6 +34,13 @@ cxx_library( "cxx.h": "include/cxx.h", }, exported_linker_flags = ["-lstdc++"], + deps = [":symbols"], +) + +rust_library( + name = "symbols", + srcs = glob(["src/macros/**", "src/symbols/**"]), + crate_root = "src/symbols/lib.rs", ) rust_library( diff --git a/src/assert.rs b/src/assert.rs deleted file mode 100644 index 738e5bb..0000000 --- a/src/assert.rs +++ /dev/null @@ -1,5 +0,0 @@ -macro_rules! const_assert_eq { - ($left:expr, $right:expr $(,)?) => { - const _: [(); $left] = [(); $right]; - }; -} diff --git a/src/concat.rs b/src/concat.rs deleted file mode 100644 index e67e50d..0000000 --- a/src/concat.rs +++ /dev/null @@ -1,6 +0,0 @@ -macro_rules! attr { - (#[$name:ident = $value:expr] $($rest:tt)*) => { - #[$name = $value] - $($rest)* - }; -} diff --git a/src/exception.rs b/src/exception.rs index 52f0cf6..125e484 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,5 +1,4 @@ use std::fmt::{self, Debug, Display}; -use std::slice; /// Exception thrown from an `extern "C"` function. #[derive(Debug)] @@ -20,10 +19,3 @@ impl Exception { &self.what } } - -#[export_name = "cxxbridge03$exception"] -unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { - let slice = slice::from_raw_parts(ptr, len); - let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); - Box::leak(boxed).as_ptr() -} diff --git a/src/lib.rs b/src/lib.rs index 37fc5ef..86318c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,9 +363,7 @@ extern crate link_cplusplus; #[macro_use] -mod assert; -#[macro_use] -mod concat; +mod macros; mod cxx_string; mod cxx_vector; @@ -380,6 +378,9 @@ mod rust_vec; mod unique_ptr; mod unwind; +#[cfg(not(no_export_symbols))] +mod symbols; + pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; diff --git a/src/macros/assert.rs b/src/macros/assert.rs new file mode 100644 index 0000000..738e5bb --- /dev/null +++ b/src/macros/assert.rs @@ -0,0 +1,5 @@ +macro_rules! const_assert_eq { + ($left:expr, $right:expr $(,)?) => { + const _: [(); $left] = [(); $right]; + }; +} diff --git a/src/macros/concat.rs b/src/macros/concat.rs new file mode 100644 index 0000000..e67e50d --- /dev/null +++ b/src/macros/concat.rs @@ -0,0 +1,6 @@ +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} diff --git a/src/macros/mod.rs b/src/macros/mod.rs new file mode 100644 index 0000000..d12d96b --- /dev/null +++ b/src/macros/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod assert; +#[macro_use] +mod concat; diff --git a/src/rust_str.rs b/src/rust_str.rs index fe11473..b944ede 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -25,10 +25,4 @@ impl RustStr { } } -#[export_name = "cxxbridge03$str$valid"] -unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { - let slice = slice::from_raw_parts(ptr, len); - str::from_utf8(slice).is_ok() -} - const_assert_eq!(mem::size_of::>(), mem::size_of::()); diff --git a/src/rust_string.rs b/src/rust_string.rs index 6f3e64b..a923ced 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,7 +1,4 @@ -use std::mem::{self, ManuallyDrop, MaybeUninit}; -use std::ptr; -use std::slice; -use std::str; +use std::mem; #[repr(C)] pub struct RustString { @@ -30,46 +27,5 @@ impl RustString { } } -#[export_name = "cxxbridge03$string$new"] -unsafe extern "C" fn string_new(this: &mut MaybeUninit) { - ptr::write(this.as_mut_ptr(), String::new()); -} - -#[export_name = "cxxbridge03$string$clone"] -unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { - ptr::write(this.as_mut_ptr(), other.clone()); -} - -#[export_name = "cxxbridge03$string$from"] -unsafe extern "C" fn string_from( - this: &mut MaybeUninit, - ptr: *const u8, - len: usize, -) -> bool { - let slice = slice::from_raw_parts(ptr, len); - match str::from_utf8(slice) { - Ok(s) => { - ptr::write(this.as_mut_ptr(), s.to_owned()); - true - } - Err(_) => false, - } -} - -#[export_name = "cxxbridge03$string$drop"] -unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { - ManuallyDrop::drop(this); -} - -#[export_name = "cxxbridge03$string$ptr"] -unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { - this.as_ptr() -} - -#[export_name = "cxxbridge03$string$len"] -unsafe extern "C" fn string_len(this: &String) -> usize { - this.len() -} - const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::()); const_assert_eq!(mem::align_of::(), mem::align_of::()); diff --git a/src/rust_vec.rs b/src/rust_vec.rs index c17310d..4c5035d 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,6 +1,3 @@ -use std::mem; -use std::ptr; - #[repr(C)] pub struct RustVec { repr: Vec, @@ -39,54 +36,3 @@ impl RustVec { self.repr.as_ptr() } } - -macro_rules! rust_vec_shims_for_primitive { - ($ty:ident) => { - const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); - const_assert_eq!(mem::align_of::(), mem::align_of::>()); - - const _: () = { - attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")] - unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { - ptr::write(this, RustVec::new()); - } - } - attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")] - unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { - ptr::drop_in_place(this); - } - } - attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")] - unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { - (*this).len() - } - } - attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")] - unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { - (*this).as_ptr() - } - } - attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")] - unsafe extern "C" fn __stride() -> usize { - mem::size_of::<$ty>() - } - } - }; - }; -} - -rust_vec_shims_for_primitive!(u8); -rust_vec_shims_for_primitive!(u16); -rust_vec_shims_for_primitive!(u32); -rust_vec_shims_for_primitive!(u64); -rust_vec_shims_for_primitive!(i8); -rust_vec_shims_for_primitive!(i16); -rust_vec_shims_for_primitive!(i32); -rust_vec_shims_for_primitive!(i64); -rust_vec_shims_for_primitive!(f32); -rust_vec_shims_for_primitive!(f64); diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs new file mode 100644 index 0000000..849db3b --- /dev/null +++ b/src/symbols/exception.rs @@ -0,0 +1,8 @@ +use std::slice; + +#[export_name = "cxxbridge03$exception"] +unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { + let slice = slice::from_raw_parts(ptr, len); + let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); + Box::leak(boxed).as_ptr() +} diff --git a/src/symbols/lib.rs b/src/symbols/lib.rs new file mode 100644 index 0000000..2c052ec --- /dev/null +++ b/src/symbols/lib.rs @@ -0,0 +1,5 @@ +#[path = "../macros/mod.rs"] +#[macro_use] +mod macros; + +include!("mod.rs"); diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs new file mode 100644 index 0000000..a9d158d --- /dev/null +++ b/src/symbols/mod.rs @@ -0,0 +1,4 @@ +mod exception; +mod rust_str; +mod rust_string; +mod rust_vec; diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs new file mode 100644 index 0000000..6dc04ac --- /dev/null +++ b/src/symbols/rust_str.rs @@ -0,0 +1,8 @@ +use std::slice; +use std::str; + +#[export_name = "cxxbridge03$str$valid"] +unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { + let slice = slice::from_raw_parts(ptr, len); + str::from_utf8(slice).is_ok() +} diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs new file mode 100644 index 0000000..63d4ba7 --- /dev/null +++ b/src/symbols/rust_string.rs @@ -0,0 +1,45 @@ +use std::mem::{ManuallyDrop, MaybeUninit}; +use std::ptr; +use std::slice; +use std::str; + +#[export_name = "cxxbridge03$string$new"] +unsafe extern "C" fn string_new(this: &mut MaybeUninit) { + ptr::write(this.as_mut_ptr(), String::new()); +} + +#[export_name = "cxxbridge03$string$clone"] +unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { + ptr::write(this.as_mut_ptr(), other.clone()); +} + +#[export_name = "cxxbridge03$string$from"] +unsafe extern "C" fn string_from( + this: &mut MaybeUninit, + ptr: *const u8, + len: usize, +) -> bool { + let slice = slice::from_raw_parts(ptr, len); + match str::from_utf8(slice) { + Ok(s) => { + ptr::write(this.as_mut_ptr(), s.to_owned()); + true + } + Err(_) => false, + } +} + +#[export_name = "cxxbridge03$string$drop"] +unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { + ManuallyDrop::drop(this); +} + +#[export_name = "cxxbridge03$string$ptr"] +unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { + this.as_ptr() +} + +#[export_name = "cxxbridge03$string$len"] +unsafe extern "C" fn string_len(this: &String) -> usize { + this.len() +} diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs new file mode 100644 index 0000000..712a9e8 --- /dev/null +++ b/src/symbols/rust_vec.rs @@ -0,0 +1,65 @@ +use std::mem; +use std::ptr; + +#[repr(C)] +pub struct RustVec { + repr: Vec, +} + +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} + +macro_rules! rust_vec_shims_for_primitive { + ($ty:ident) => { + const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); + const_assert_eq!(mem::align_of::(), mem::align_of::>()); + + const _: () = { + attr! { + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")] + unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { + ptr::write(this, RustVec { repr: Vec::new() }); + } + } + attr! { + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")] + unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { + ptr::drop_in_place(this); + } + } + attr! { + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")] + unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { + (*this).repr.len() + } + } + attr! { + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")] + unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { + (*this).repr.as_ptr() + } + } + attr! { + #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")] + unsafe extern "C" fn __stride() -> usize { + mem::size_of::<$ty>() + } + } + }; + }; +} + +rust_vec_shims_for_primitive!(u8); +rust_vec_shims_for_primitive!(u16); +rust_vec_shims_for_primitive!(u32); +rust_vec_shims_for_primitive!(u64); +rust_vec_shims_for_primitive!(i8); +rust_vec_shims_for_primitive!(i16); +rust_vec_shims_for_primitive!(i32); +rust_vec_shims_for_primitive!(i64); +rust_vec_shims_for_primitive!(f32); +rust_vec_shims_for_primitive!(f64); From 4ee9ecabbdcc283a901a13ffd57c2c4eb64ffcd1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 14:48:25 +0000 Subject: [PATCH 493/2232] Re-enable Buck CI --- diff --git a/.travis.yml b/.travis.yml index 0243407..f65f7f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,3 @@ matrix: rust: 1.42.0 script: - cargo run --manifest-path demo-rs/Cargo.toml - - # https://github.com/dtolnay/cxx/pull/167 - allow_failures: - - name: Buck From 7997d07818aaebf990bc79bcb27ee4a64d5e757d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 14:55:08 +0000 Subject: [PATCH 494/2232] Avoid second lib.rs in the same src directory This was throwing off the Bazel build because it didn't know which one to make the crate root. --- diff --git a/BUCK b/BUCK index acf3a4c..241d7b3 100644 --- a/BUCK +++ b/BUCK @@ -40,7 +40,7 @@ cxx_library( rust_library( name = "symbols", srcs = glob(["src/macros/**", "src/symbols/**"]), - crate_root = "src/symbols/lib.rs", + crate_root = "src/symbols/symbols.rs", ) rust_library( diff --git a/src/symbols/lib.rs b/src/symbols/lib.rs deleted file mode 100644 index 2c052ec..0000000 --- a/src/symbols/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[path = "../macros/mod.rs"] -#[macro_use] -mod macros; - -include!("mod.rs"); diff --git a/src/symbols/symbols.rs b/src/symbols/symbols.rs new file mode 100644 index 0000000..2c052ec --- /dev/null +++ b/src/symbols/symbols.rs @@ -0,0 +1,5 @@ +#[path = "../macros/mod.rs"] +#[macro_use] +mod macros; + +include!("mod.rs"); From f336b3b3435258570e85f626063f4ed8740bd92a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 15:45:54 +0000 Subject: [PATCH 495/2232] Add support for rust::Vec --- diff --git a/src/cxx.cc b/src/cxx.cc index 5f7ac6a..eb86d3d 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -266,7 +266,7 @@ void cxxbridge03$unique_ptr$std$string$drop( } // Usize and isize are the same type as one of the below. -#define FOR_EACH_SIZED_PRIMITIVE(MACRO) \ +#define FOR_EACH_NUMERIC(MACRO) \ MACRO(u8, uint8_t) \ MACRO(u16, uint16_t) \ MACRO(u32, uint32_t) \ @@ -278,18 +278,22 @@ void cxxbridge03$unique_ptr$std$string$drop( MACRO(f32, float) \ MACRO(f64, double) -#define FOR_EACH_PRIMITIVE(MACRO) \ - FOR_EACH_SIZED_PRIMITIVE(MACRO) \ +#define FOR_EACH_STD_VECTOR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ MACRO(usize, size_t) \ MACRO(isize, rust::isize) +#define FOR_EACH_RUST_VEC(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) + extern "C" { -FOR_EACH_PRIMITIVE(STD_VECTOR_OPS) -FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) +FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) } // extern "C" namespace rust { inline namespace cxxbridge03 { -FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_OPS) } // namespace cxxbridge03 } // namespace rust diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 712a9e8..9ce87ab 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -53,6 +53,7 @@ macro_rules! rust_vec_shims_for_primitive { }; } +rust_vec_shims_for_primitive!(bool); rust_vec_shims_for_primitive!(u8); rust_vec_shims_for_primitive!(u16); rust_vec_shims_for_primitive!(u32); From d776519a75a5e26e3259f8008ea228f83193cdf8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Apr 30 2020 15:55:43 +0000 Subject: [PATCH 496/2232] Merge pull request #169 from dtolnay/vecbool Add support for rust::Vec --- diff --git a/src/cxx.cc b/src/cxx.cc index 5f7ac6a..eb86d3d 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -266,7 +266,7 @@ void cxxbridge03$unique_ptr$std$string$drop( } // Usize and isize are the same type as one of the below. -#define FOR_EACH_SIZED_PRIMITIVE(MACRO) \ +#define FOR_EACH_NUMERIC(MACRO) \ MACRO(u8, uint8_t) \ MACRO(u16, uint16_t) \ MACRO(u32, uint32_t) \ @@ -278,18 +278,22 @@ void cxxbridge03$unique_ptr$std$string$drop( MACRO(f32, float) \ MACRO(f64, double) -#define FOR_EACH_PRIMITIVE(MACRO) \ - FOR_EACH_SIZED_PRIMITIVE(MACRO) \ +#define FOR_EACH_STD_VECTOR(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ MACRO(usize, size_t) \ MACRO(isize, rust::isize) +#define FOR_EACH_RUST_VEC(MACRO) \ + FOR_EACH_NUMERIC(MACRO) \ + MACRO(bool, bool) + extern "C" { -FOR_EACH_PRIMITIVE(STD_VECTOR_OPS) -FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_EXTERNS) +FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) } // extern "C" namespace rust { inline namespace cxxbridge03 { -FOR_EACH_SIZED_PRIMITIVE(RUST_VEC_OPS) +FOR_EACH_RUST_VEC(RUST_VEC_OPS) } // namespace cxxbridge03 } // namespace rust diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 712a9e8..9ce87ab 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -53,6 +53,7 @@ macro_rules! rust_vec_shims_for_primitive { }; } +rust_vec_shims_for_primitive!(bool); rust_vec_shims_for_primitive!(u8); rust_vec_shims_for_primitive!(u16); rust_vec_shims_for_primitive!(u32); From c03402aee567f6594de660c77f7ca3daf0862d5f Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Apr 30 2020 21:12:37 +0000 Subject: [PATCH 497/2232] Support C-style enums This adds support for passing C-style enums between Rust and C++. We use the Rust representation for enums suggested by dtolnay in #132. Note that as this does not use real enums, Rust code cannot treat them as normal enums, e.g., by converting them to integers. But common uses such as pattern matching remain unchanged. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8869849..eb72ba4 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,7 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -64,6 +64,10 @@ pub(super) fn gen( out.next_section(); write_struct(out, strct); } + Api::Enum(enm) => { + out.next_section(); + write_enum(out, enm); + } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { out.next_section(); @@ -353,6 +357,22 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, "}};"); } +fn write_enum(out: &mut OutFile, enm: &Enum) { + for line in enm.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "enum class {} : uint32_t {{", enm.ident); + for variant in &enm.variants { + write!(out, " "); + write!(out, "{}", variant.ident); + if let Some(discriminant) = &variant.discriminant { + write!(out, " = {}", discriminant); + } + writeln!(out, ","); + } + writeln!(out, "}};"); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 15a091b..788a156 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -2,7 +2,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -36,6 +36,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { match api { Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)), Api::CxxFunction(efn) => { expanded.extend(expand_cxx_function_shim(namespace, efn, types)); @@ -123,6 +124,34 @@ fn expand_struct(strct: &Struct) -> TokenStream { } } +fn expand_enum(enm: &Enum) -> TokenStream { + let ident = &enm.ident; + let doc = &enm.doc; + let variants = enm.variants.iter().scan(0, |next_discriminant, variant| { + // This span on the pub makes "private type in public interface" errors + // appear in the right place. + let vis = Token![pub](variant.ident.span()); + let variant_ident = &variant.ident; + let discriminant = match variant.discriminant { + None => *next_discriminant, + Some(val) => val, + }; + *next_discriminant = discriminant + 1; + Some(quote!( #vis const #variant_ident: Self = #ident(#discriminant))) + }); + quote! { + #doc + #[derive(Copy, Clone, PartialEq, Eq)] + #[repr(transparent)] + pub struct #ident(u32); + + #[allow(non_upper_case_globals)] + impl #ident { + #(#variants;)* + } + } +} + fn expand_cxx_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; diff --git a/syntax/check.rs b/syntax/check.rs index 3e121b5..321fb1a 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,9 +1,11 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{ - error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, + error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, + Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; +use std::collections::HashSet; use std::fmt::Display; use syn::{Error, Result}; @@ -41,6 +43,7 @@ fn do_typecheck(cx: &mut Check) { for api in cx.apis { match api { Api::Struct(strct) => check_api_struct(cx, strct), + Api::Enum(enm) => check_api_enum(cx, enm), Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} @@ -68,6 +71,7 @@ impl Check<'_> { fn check_type_ident(cx: &mut Check, ident: &Ident) { 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) { @@ -188,6 +192,28 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } } +fn check_api_enum(cx: &mut Check, enm: &Enum) { + check_reserved_name(cx, &enm.ident); + + if enm.variants.is_empty() { + let span = span_for_enum_error(enm); + cx.error(span, "enums without any variants are not supported"); + } + + let mut discriminants = HashSet::new(); + enm.variants.iter().fold(0, |next_discriminant, variant| { + let discriminant = match variant.discriminant { + None => next_discriminant, + Some(val) => val, + }; + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + cx.error(span_for_enum_error(enm), msg); + } + discriminant + 1 + }); +} + fn check_api_type(cx: &mut Check, ty: &ExternType) { check_reserved_name(cx, &ty.ident); } @@ -320,6 +346,13 @@ fn span_for_struct_error(strct: &Struct) -> TokenStream { 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; diff --git a/syntax/ident.rs b/syntax/ident.rs index 84be4cb..d5bb33f 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -23,6 +23,12 @@ pub(crate) fn check_all(apis: &[Api], errors: &mut Vec) { errors.extend(check(&field.ident).err()); } } + Api::Enum(enm) => { + errors.extend(check(&enm.ident).err()); + for variant in &enm.variants { + errors.extend(check(&variant.ident).err()); + } + } Api::CxxType(ety) | Api::RustType(ety) => { errors.extend(check(&ety.ident).err()); } diff --git a/syntax/mod.rs b/syntax/mod.rs index a4b0ac4..9b1a7e5 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -29,6 +29,7 @@ pub use self::types::Types; pub enum Api { Include(LitStr), Struct(Struct), + Enum(Enum), CxxType(ExternType), CxxFunction(ExternFn), RustType(ExternType), @@ -50,6 +51,14 @@ pub struct Struct { pub fields: Vec, } +pub struct Enum { + pub doc: Doc, + pub enum_token: Token![enum], + pub ident: Ident, + pub brace_token: Brace, + pub variants: Vec, +} + pub struct ExternFn { pub lang: Lang, pub doc: Doc, @@ -83,6 +92,11 @@ pub struct Receiver { pub shorthand: bool, } +pub struct Variant { + pub ident: Ident, + pub discriminant: Option, +} + pub enum Type { Ident(Ident), RustBox(Box), diff --git a/syntax/parse.rs b/syntax/parse.rs index a425c56..995b21f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,14 +1,15 @@ use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, - Ty1, Type, Var, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, + Struct, Ty1, Type, Var, Variant, }; use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Ident, - Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, - Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments, + Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + Variant as RustVariant, }; pub mod kw { @@ -23,6 +24,10 @@ pub fn parse_items(items: Vec) -> Result> { let strct = parse_struct(item)?; apis.push(strct); } + Item::Enum(item) => { + let enm = parse_enum(item)?; + apis.push(enm); + } Item::ForeignMod(foreign_mod) => { let functions = parse_foreign_mod(foreign_mod)?; apis.extend(functions); @@ -78,6 +83,75 @@ fn parse_struct(item: ItemStruct) -> Result { })) } +fn parse_enum(item: ItemEnum) -> Result { + let generics = &item.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + let enum_token = item.enum_token; + let ident = &item.ident; + let where_clause = &generics.where_clause; + let span = quote!(#enum_token #ident #generics #where_clause); + return Err(Error::new_spanned( + span, + "enums with generic parameters are not allowed", + )); + } + + let mut doc = Doc::new(); + attrs::parse(&item.attrs, &mut doc, None)?; + + for variant in &item.variants { + match &variant.fields { + Fields::Unit => {} + _ => { + return Err(Error::new_spanned( + variant, + "enums with data are not allowed", + )) + } + } + } + + Ok(Api::Enum(Enum { + doc, + enum_token: item.enum_token, + ident: item.ident, + brace_token: item.brace_token, + variants: item + .variants + .into_iter() + .map(parse_variant) + .collect::>()?, + })) +} + +fn parse_variant(variant: RustVariant) -> Result { + match &variant.discriminant { + None => Ok(Variant { + ident: variant.ident, + discriminant: None, + }), + Some(( + _, + Expr::Lit(ExprLit { + lit: Lit::Int(n), .. + }), + )) => match n.base10_digits().parse() { + Ok(val) => Ok(Variant { + ident: variant.ident, + discriminant: Some(val), + }), + Err(_) => Err(Error::new_spanned( + variant, + "cannot parse enum discriminant as an integer", + )), + }, + _ => Err(Error::new_spanned( + variant, + "enums with non-integer literal discriminants are not supported", + )), + } +} + fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let lang = parse_lang(foreign_mod.abi)?; let api_type = match lang { diff --git a/syntax/types.rs b/syntax/types.rs index f3aaa16..bb5a843 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, ExternType, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type}; use proc_macro2::Ident; use quote::quote; use std::collections::BTreeMap as Map; @@ -9,6 +9,7 @@ use syn::{Error, Result}; pub struct Types<'a> { pub all: Set<'a, Type>, pub structs: Map, + pub enums: Map, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, } @@ -17,6 +18,7 @@ impl<'a> Types<'a> { pub fn collect(apis: &'a [Api]) -> Result { let mut all = Set::new(); let mut structs = Map::new(); + let mut enums = Map::new(); let mut cxx = Set::new(); let mut rust = Set::new(); @@ -46,7 +48,11 @@ impl<'a> Types<'a> { Api::Include(_) => {} Api::Struct(strct) => { let ident = &strct.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_struct(strct)); } structs.insert(strct.ident.clone(), strct); @@ -54,16 +60,35 @@ impl<'a> Types<'a> { visit(&mut all, &field.ty); } } + Api::Enum(enm) => { + let ident = &enm.ident; + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { + return Err(duplicate_enum(enm)); + } + enums.insert(enm.ident.clone(), enm); + } Api::CxxType(ety) => { let ident = &ety.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_type(ety)); } cxx.insert(ident); } Api::RustType(ety) => { let ident = &ety.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_type(ety)); } rust.insert(ident); @@ -82,6 +107,7 @@ impl<'a> Types<'a> { Ok(Types { all, structs, + enums, cxx, rust, }) @@ -126,6 +152,13 @@ fn duplicate_struct(strct: &Struct) -> Error { Error::new_spanned(range, "duplicate type") } +fn duplicate_enum(enm: &Enum) -> Error { + let enum_token = enm.enum_token; + let ident = &enm.ident; + let range = quote!(#enum_token #ident); + Error::new_spanned(range, "duplicate type") +} + fn duplicate_type(ety: &ExternType) -> Error { let type_token = ety.type_token; let ident = &ety.ident; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 01dc404..0162c9b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -13,6 +13,12 @@ pub mod ffi { z: usize, } + enum Enum { + AVal, + BVal = 2020, + CVal, + } + extern "C" { include!("tests/ffi/tests.h"); @@ -36,6 +42,7 @@ pub mod ffi { fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; + fn c_return_enum(n: u32) -> Enum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -57,6 +64,7 @@ pub mod ffi { fn c_take_ref_rust_vec(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); + fn c_take_enum(e: Enum); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; @@ -92,6 +100,7 @@ pub mod ffi { fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; + fn r_return_enum(n: u32) -> Enum; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -105,6 +114,7 @@ pub mod ffi { fn r_take_unique_ptr_string(s: UniquePtr); fn r_take_rust_vec(v: Vec); fn r_take_ref_rust_vec(v: &Vec); + fn r_take_enum(e: Enum); fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; @@ -198,6 +208,16 @@ fn r_return_sum(n1: usize, n2: usize) -> usize { n1 + n2 } +fn r_return_enum(n: u32) -> ffi::Enum { + if n <= 0 { + ffi::Enum::AVal + } else if n <= 2020 { + ffi::Enum::BVal + } else { + ffi::Enum::CVal + } +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } @@ -247,6 +267,10 @@ fn r_take_ref_rust_vec(v: &Vec) { let _ = v; } +fn r_take_enum(e: ffi::Enum) { + let _ = e; +} + fn r_try_return_void() -> Result<(), Error> { Ok(()) } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2b5431f..71c90b0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -106,6 +106,16 @@ size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } +Enum c_return_enum(uint32_t n) { + if (n <= static_cast(Enum::AVal)) { + return Enum::AVal; + } else if (n <= static_cast(Enum::BVal)) { + return Enum::BVal; + } else { + return Enum::CVal; + } +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -238,6 +248,12 @@ void c_take_callback(rust::Fn callback) { callback("2020"); } +void c_take_enum(Enum e) { + if (e == Enum::AVal) { + cxx_test_suite_set_correct(); + } +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } @@ -295,6 +311,9 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(*r_return_unique_ptr_string() == "2020"); ASSERT(r_return_identity(2020) == 2020); ASSERT(r_return_sum(2020, 1) == 2021); + ASSERT(r_return_enum(0) == Enum::AVal); + ASSERT(r_return_enum(1) == Enum::BVal); + ASSERT(r_return_enum(2021) == Enum::CVal); r_take_primitive(2020); r_take_shared(Shared{2020}); @@ -306,6 +325,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + r_take_enum(Enum::AVal); ASSERT(r_try_return_primitive() == 2020); try { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index da8ca56..d3b7d38 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -7,6 +7,7 @@ namespace tests { struct R; struct Shared; +enum class Enum : uint32_t; class C { public: @@ -40,6 +41,7 @@ rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); +Enum c_return_enum(uint32_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -61,6 +63,7 @@ void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); void c_take_callback(rust::Fn callback); +void c_take_enum(Enum e); void c_try_return_void(); size_t c_try_return_primitive(); diff --git a/tests/test.rs b/tests/test.rs index d6850aa..1156d5d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -51,6 +51,18 @@ fn test_c_return() { ); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); + match ffi::c_return_enum(0) { + ffi::Enum::AVal => {} + _ => assert!(false), + } + match ffi::c_return_enum(1) { + ffi::Enum::BVal => {} + _ => assert!(false), + } + match ffi::c_return_enum(2021) { + ffi::Enum::CVal => {} + _ => assert!(false), + } } #[test] @@ -106,6 +118,7 @@ fn test_c_take() { ])); check!(ffi::c_take_ref_rust_vec(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); + check!(ffi::c_take_enum(ffi::Enum::AVal)); } #[test] diff --git a/tests/ui/data_enums.rs b/tests/ui/data_enums.rs new file mode 100644 index 0000000..aa23200 --- /dev/null +++ b/tests/ui/data_enums.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field(u64), + } +} + +fn main() {} diff --git a/tests/ui/data_enums.stderr b/tests/ui/data_enums.stderr new file mode 100644 index 0000000..118514e --- /dev/null +++ b/tests/ui/data_enums.stderr @@ -0,0 +1,5 @@ +error: enums with data are not allowed + --> $DIR/data_enums.rs:4:9 + | +4 | Field(u64), + | ^^^^^^^^^^ diff --git a/tests/ui/duplicate_enum_discriminants.rs b/tests/ui/duplicate_enum_discriminants.rs new file mode 100644 index 0000000..ec3d61a --- /dev/null +++ b/tests/ui/duplicate_enum_discriminants.rs @@ -0,0 +1,15 @@ +#[cxx::bridge] +mod ffi { + enum A { + V1 = 10, + V2 = 10, + } + + enum B { + V1 = 10, + V2, + V3 = 11, + } +} + +fn main() {} diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr new file mode 100644 index 0000000..f5a879f --- /dev/null +++ b/tests/ui/duplicate_enum_discriminants.stderr @@ -0,0 +1,18 @@ +error: discriminant value `10` already exists + --> $DIR/duplicate_enum_discriminants.rs:3:5 + | +3 | / enum A { +4 | | V1 = 10, +5 | | V2 = 10, +6 | | } + | |_____^ + +error: discriminant value `11` already exists + --> $DIR/duplicate_enum_discriminants.rs:8:5 + | +8 | / enum B { +9 | | V1 = 10, +10 | | V2, +11 | | V3 = 11, +12 | | } + | |_____^ diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs new file mode 100644 index 0000000..a9ad533 --- /dev/null +++ b/tests/ui/empty_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + + } +} + +fn main() {} diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr new file mode 100644 index 0000000..c73578a --- /dev/null +++ b/tests/ui/empty_enum.stderr @@ -0,0 +1,7 @@ +error: enums without any variants are not supported + --> $DIR/empty_enum.rs:3:5 + | +3 | / enum A { +4 | | +5 | | } + | |_____^ diff --git a/tests/ui/enum_match_without_wildcard.rs b/tests/ui/enum_match_without_wildcard.rs new file mode 100644 index 0000000..1a11942 --- /dev/null +++ b/tests/ui/enum_match_without_wildcard.rs @@ -0,0 +1,16 @@ +#[cxx::bridge] +mod ffi { + enum A { + FieldA, + FieldB, + } +} + +fn main() {} + +fn matcher(a: ffi::A) -> u32 { + match a { + ffi::A::FieldA => 2020, + ffi::A::FieldB => 2021, + } +} diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr new file mode 100644 index 0000000..11d132d --- /dev/null +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -0,0 +1,10 @@ +error[E0004]: non-exhaustive patterns: `A(2u32..=std::u32::MAX)` not covered + --> $DIR/enum_match_without_wildcard.rs:12:11 + | +1 | #[cxx::bridge] + | -------------- `ffi::A` defined here +... +12 | match a { + | ^ pattern `A(2u32..=std::u32::MAX)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/tests/ui/generic_enum.rs b/tests/ui/generic_enum.rs new file mode 100644 index 0000000..808856e --- /dev/null +++ b/tests/ui/generic_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field, + } +} + +fn main() {} diff --git a/tests/ui/generic_enum.stderr b/tests/ui/generic_enum.stderr new file mode 100644 index 0000000..bf267f7 --- /dev/null +++ b/tests/ui/generic_enum.stderr @@ -0,0 +1,5 @@ +error: enums with generic parameters are not allowed + --> $DIR/generic_enum.rs:3:5 + | +3 | enum A { + | ^^^^^^^^^ diff --git a/tests/ui/non_integer_discriminant_enum.rs b/tests/ui/non_integer_discriminant_enum.rs new file mode 100644 index 0000000..388b463 --- /dev/null +++ b/tests/ui/non_integer_discriminant_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field = 2020 + 1, + } +} + +fn main() {} diff --git a/tests/ui/non_integer_discriminant_enum.stderr b/tests/ui/non_integer_discriminant_enum.stderr new file mode 100644 index 0000000..7f70f25 --- /dev/null +++ b/tests/ui/non_integer_discriminant_enum.stderr @@ -0,0 +1,5 @@ +error: enums with non-integer literal discriminants are not supported + --> $DIR/non_integer_discriminant_enum.rs:4:9 + | +4 | Field = 2020 + 1, + | ^^^^^^^^^^^^^^^^ From 2b12b3211355d11a2e1b3089bb1c1e25b71347a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:03:25 +0000 Subject: [PATCH 498/2232] Update PR 170 compiletests at nightly rustc --- diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 11d132d..12b8429 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -8,3 +8,4 @@ error[E0004]: non-exhaustive patterns: `A(2u32..=std::u32::MAX)` not covered | ^ pattern `A(2u32..=std::u32::MAX)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `ffi::A` From 02d58bb5932f50258e83515037fd27563e167285 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:49:50 +0000 Subject: [PATCH 499/2232] Merge pull request #170 from jgalenson/enums Support C-style enums --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8869849..eb72ba4 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,7 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -64,6 +64,10 @@ pub(super) fn gen( out.next_section(); write_struct(out, strct); } + Api::Enum(enm) => { + out.next_section(); + write_enum(out, enm); + } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { out.next_section(); @@ -353,6 +357,22 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, "}};"); } +fn write_enum(out: &mut OutFile, enm: &Enum) { + for line in enm.doc.to_string().lines() { + writeln!(out, "//{}", line); + } + writeln!(out, "enum class {} : uint32_t {{", enm.ident); + for variant in &enm.variants { + write!(out, " "); + write!(out, "{}", variant.ident); + if let Some(discriminant) = &variant.discriminant { + write!(out, " = {}", discriminant); + } + writeln!(out, ","); + } + writeln!(out, "}};"); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 15a091b..788a156 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -2,7 +2,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -36,6 +36,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { match api { Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)), Api::CxxFunction(efn) => { expanded.extend(expand_cxx_function_shim(namespace, efn, types)); @@ -123,6 +124,34 @@ fn expand_struct(strct: &Struct) -> TokenStream { } } +fn expand_enum(enm: &Enum) -> TokenStream { + let ident = &enm.ident; + let doc = &enm.doc; + let variants = enm.variants.iter().scan(0, |next_discriminant, variant| { + // This span on the pub makes "private type in public interface" errors + // appear in the right place. + let vis = Token![pub](variant.ident.span()); + let variant_ident = &variant.ident; + let discriminant = match variant.discriminant { + None => *next_discriminant, + Some(val) => val, + }; + *next_discriminant = discriminant + 1; + Some(quote!( #vis const #variant_ident: Self = #ident(#discriminant))) + }); + quote! { + #doc + #[derive(Copy, Clone, PartialEq, Eq)] + #[repr(transparent)] + pub struct #ident(u32); + + #[allow(non_upper_case_globals)] + impl #ident { + #(#variants;)* + } + } +} + fn expand_cxx_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; diff --git a/syntax/check.rs b/syntax/check.rs index 3e121b5..321fb1a 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,9 +1,11 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::{ - error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, + error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, + Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; +use std::collections::HashSet; use std::fmt::Display; use syn::{Error, Result}; @@ -41,6 +43,7 @@ fn do_typecheck(cx: &mut Check) { for api in cx.apis { match api { Api::Struct(strct) => check_api_struct(cx, strct), + Api::Enum(enm) => check_api_enum(cx, enm), Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} @@ -68,6 +71,7 @@ impl Check<'_> { fn check_type_ident(cx: &mut Check, ident: &Ident) { 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) { @@ -188,6 +192,28 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } } +fn check_api_enum(cx: &mut Check, enm: &Enum) { + check_reserved_name(cx, &enm.ident); + + if enm.variants.is_empty() { + let span = span_for_enum_error(enm); + cx.error(span, "enums without any variants are not supported"); + } + + let mut discriminants = HashSet::new(); + enm.variants.iter().fold(0, |next_discriminant, variant| { + let discriminant = match variant.discriminant { + None => next_discriminant, + Some(val) => val, + }; + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + cx.error(span_for_enum_error(enm), msg); + } + discriminant + 1 + }); +} + fn check_api_type(cx: &mut Check, ty: &ExternType) { check_reserved_name(cx, &ty.ident); } @@ -320,6 +346,13 @@ fn span_for_struct_error(strct: &Struct) -> TokenStream { 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; diff --git a/syntax/ident.rs b/syntax/ident.rs index 84be4cb..d5bb33f 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -23,6 +23,12 @@ pub(crate) fn check_all(apis: &[Api], errors: &mut Vec) { errors.extend(check(&field.ident).err()); } } + Api::Enum(enm) => { + errors.extend(check(&enm.ident).err()); + for variant in &enm.variants { + errors.extend(check(&variant.ident).err()); + } + } Api::CxxType(ety) | Api::RustType(ety) => { errors.extend(check(&ety.ident).err()); } diff --git a/syntax/mod.rs b/syntax/mod.rs index a4b0ac4..9b1a7e5 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -29,6 +29,7 @@ pub use self::types::Types; pub enum Api { Include(LitStr), Struct(Struct), + Enum(Enum), CxxType(ExternType), CxxFunction(ExternFn), RustType(ExternType), @@ -50,6 +51,14 @@ pub struct Struct { pub fields: Vec, } +pub struct Enum { + pub doc: Doc, + pub enum_token: Token![enum], + pub ident: Ident, + pub brace_token: Brace, + pub variants: Vec, +} + pub struct ExternFn { pub lang: Lang, pub doc: Doc, @@ -83,6 +92,11 @@ pub struct Receiver { pub shorthand: bool, } +pub struct Variant { + pub ident: Ident, + pub discriminant: Option, +} + pub enum Type { Ident(Ident), RustBox(Box), diff --git a/syntax/parse.rs b/syntax/parse.rs index a425c56..995b21f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,14 +1,15 @@ use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, - Ty1, Type, Var, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, + Struct, Ty1, Type, Var, Variant, }; use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Ident, - Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, - Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments, + Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + Variant as RustVariant, }; pub mod kw { @@ -23,6 +24,10 @@ pub fn parse_items(items: Vec) -> Result> { let strct = parse_struct(item)?; apis.push(strct); } + Item::Enum(item) => { + let enm = parse_enum(item)?; + apis.push(enm); + } Item::ForeignMod(foreign_mod) => { let functions = parse_foreign_mod(foreign_mod)?; apis.extend(functions); @@ -78,6 +83,75 @@ fn parse_struct(item: ItemStruct) -> Result { })) } +fn parse_enum(item: ItemEnum) -> Result { + let generics = &item.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + let enum_token = item.enum_token; + let ident = &item.ident; + let where_clause = &generics.where_clause; + let span = quote!(#enum_token #ident #generics #where_clause); + return Err(Error::new_spanned( + span, + "enums with generic parameters are not allowed", + )); + } + + let mut doc = Doc::new(); + attrs::parse(&item.attrs, &mut doc, None)?; + + for variant in &item.variants { + match &variant.fields { + Fields::Unit => {} + _ => { + return Err(Error::new_spanned( + variant, + "enums with data are not allowed", + )) + } + } + } + + Ok(Api::Enum(Enum { + doc, + enum_token: item.enum_token, + ident: item.ident, + brace_token: item.brace_token, + variants: item + .variants + .into_iter() + .map(parse_variant) + .collect::>()?, + })) +} + +fn parse_variant(variant: RustVariant) -> Result { + match &variant.discriminant { + None => Ok(Variant { + ident: variant.ident, + discriminant: None, + }), + Some(( + _, + Expr::Lit(ExprLit { + lit: Lit::Int(n), .. + }), + )) => match n.base10_digits().parse() { + Ok(val) => Ok(Variant { + ident: variant.ident, + discriminant: Some(val), + }), + Err(_) => Err(Error::new_spanned( + variant, + "cannot parse enum discriminant as an integer", + )), + }, + _ => Err(Error::new_spanned( + variant, + "enums with non-integer literal discriminants are not supported", + )), + } +} + fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let lang = parse_lang(foreign_mod.abi)?; let api_type = match lang { diff --git a/syntax/types.rs b/syntax/types.rs index f3aaa16..bb5a843 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, ExternType, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type}; use proc_macro2::Ident; use quote::quote; use std::collections::BTreeMap as Map; @@ -9,6 +9,7 @@ use syn::{Error, Result}; pub struct Types<'a> { pub all: Set<'a, Type>, pub structs: Map, + pub enums: Map, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, } @@ -17,6 +18,7 @@ impl<'a> Types<'a> { pub fn collect(apis: &'a [Api]) -> Result { let mut all = Set::new(); let mut structs = Map::new(); + let mut enums = Map::new(); let mut cxx = Set::new(); let mut rust = Set::new(); @@ -46,7 +48,11 @@ impl<'a> Types<'a> { Api::Include(_) => {} Api::Struct(strct) => { let ident = &strct.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_struct(strct)); } structs.insert(strct.ident.clone(), strct); @@ -54,16 +60,35 @@ impl<'a> Types<'a> { visit(&mut all, &field.ty); } } + Api::Enum(enm) => { + let ident = &enm.ident; + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { + return Err(duplicate_enum(enm)); + } + enums.insert(enm.ident.clone(), enm); + } Api::CxxType(ety) => { let ident = &ety.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_type(ety)); } cxx.insert(ident); } Api::RustType(ety) => { let ident = &ety.ident; - if structs.contains_key(ident) || cxx.contains(ident) || rust.contains(ident) { + if structs.contains_key(ident) + || enums.contains_key(ident) + || cxx.contains(ident) + || rust.contains(ident) + { return Err(duplicate_type(ety)); } rust.insert(ident); @@ -82,6 +107,7 @@ impl<'a> Types<'a> { Ok(Types { all, structs, + enums, cxx, rust, }) @@ -126,6 +152,13 @@ fn duplicate_struct(strct: &Struct) -> Error { Error::new_spanned(range, "duplicate type") } +fn duplicate_enum(enm: &Enum) -> Error { + let enum_token = enm.enum_token; + let ident = &enm.ident; + let range = quote!(#enum_token #ident); + Error::new_spanned(range, "duplicate type") +} + fn duplicate_type(ety: &ExternType) -> Error { let type_token = ety.type_token; let ident = &ety.ident; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 01dc404..0162c9b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -13,6 +13,12 @@ pub mod ffi { z: usize, } + enum Enum { + AVal, + BVal = 2020, + CVal, + } + extern "C" { include!("tests/ffi/tests.h"); @@ -36,6 +42,7 @@ pub mod ffi { fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; + fn c_return_enum(n: u32) -> Enum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -57,6 +64,7 @@ pub mod ffi { fn c_take_ref_rust_vec(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); fn c_take_callback(callback: fn(String) -> usize); + fn c_take_enum(e: Enum); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; @@ -92,6 +100,7 @@ pub mod ffi { fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; + fn r_return_enum(n: u32) -> Enum; fn r_take_primitive(n: usize); fn r_take_shared(shared: Shared); @@ -105,6 +114,7 @@ pub mod ffi { fn r_take_unique_ptr_string(s: UniquePtr); fn r_take_rust_vec(v: Vec); fn r_take_ref_rust_vec(v: &Vec); + fn r_take_enum(e: Enum); fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; @@ -198,6 +208,16 @@ fn r_return_sum(n1: usize, n2: usize) -> usize { n1 + n2 } +fn r_return_enum(n: u32) -> ffi::Enum { + if n <= 0 { + ffi::Enum::AVal + } else if n <= 2020 { + ffi::Enum::BVal + } else { + ffi::Enum::CVal + } +} + fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } @@ -247,6 +267,10 @@ fn r_take_ref_rust_vec(v: &Vec) { let _ = v; } +fn r_take_enum(e: ffi::Enum) { + let _ = e; +} + fn r_try_return_void() -> Result<(), Error> { Ok(()) } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2b5431f..71c90b0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -106,6 +106,16 @@ size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } +Enum c_return_enum(uint32_t n) { + if (n <= static_cast(Enum::AVal)) { + return Enum::AVal; + } else if (n <= static_cast(Enum::BVal)) { + return Enum::BVal; + } else { + return Enum::CVal; + } +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -238,6 +248,12 @@ void c_take_callback(rust::Fn callback) { callback("2020"); } +void c_take_enum(Enum e) { + if (e == Enum::AVal) { + cxx_test_suite_set_correct(); + } +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } @@ -295,6 +311,9 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(*r_return_unique_ptr_string() == "2020"); ASSERT(r_return_identity(2020) == 2020); ASSERT(r_return_sum(2020, 1) == 2021); + ASSERT(r_return_enum(0) == Enum::AVal); + ASSERT(r_return_enum(1) == Enum::BVal); + ASSERT(r_return_enum(2021) == Enum::CVal); r_take_primitive(2020); r_take_shared(Shared{2020}); @@ -306,6 +325,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + r_take_enum(Enum::AVal); ASSERT(r_try_return_primitive() == 2020); try { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index da8ca56..d3b7d38 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -7,6 +7,7 @@ namespace tests { struct R; struct Shared; +enum class Enum : uint32_t; class C { public: @@ -40,6 +41,7 @@ rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); +Enum c_return_enum(uint32_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); @@ -61,6 +63,7 @@ void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); void c_take_callback(rust::Fn callback); +void c_take_enum(Enum e); void c_try_return_void(); size_t c_try_return_primitive(); diff --git a/tests/test.rs b/tests/test.rs index d6850aa..1156d5d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -51,6 +51,18 @@ fn test_c_return() { ); assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); + match ffi::c_return_enum(0) { + ffi::Enum::AVal => {} + _ => assert!(false), + } + match ffi::c_return_enum(1) { + ffi::Enum::BVal => {} + _ => assert!(false), + } + match ffi::c_return_enum(2021) { + ffi::Enum::CVal => {} + _ => assert!(false), + } } #[test] @@ -106,6 +118,7 @@ fn test_c_take() { ])); check!(ffi::c_take_ref_rust_vec(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); + check!(ffi::c_take_enum(ffi::Enum::AVal)); } #[test] diff --git a/tests/ui/data_enums.rs b/tests/ui/data_enums.rs new file mode 100644 index 0000000..aa23200 --- /dev/null +++ b/tests/ui/data_enums.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field(u64), + } +} + +fn main() {} diff --git a/tests/ui/data_enums.stderr b/tests/ui/data_enums.stderr new file mode 100644 index 0000000..118514e --- /dev/null +++ b/tests/ui/data_enums.stderr @@ -0,0 +1,5 @@ +error: enums with data are not allowed + --> $DIR/data_enums.rs:4:9 + | +4 | Field(u64), + | ^^^^^^^^^^ diff --git a/tests/ui/duplicate_enum_discriminants.rs b/tests/ui/duplicate_enum_discriminants.rs new file mode 100644 index 0000000..ec3d61a --- /dev/null +++ b/tests/ui/duplicate_enum_discriminants.rs @@ -0,0 +1,15 @@ +#[cxx::bridge] +mod ffi { + enum A { + V1 = 10, + V2 = 10, + } + + enum B { + V1 = 10, + V2, + V3 = 11, + } +} + +fn main() {} diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr new file mode 100644 index 0000000..f5a879f --- /dev/null +++ b/tests/ui/duplicate_enum_discriminants.stderr @@ -0,0 +1,18 @@ +error: discriminant value `10` already exists + --> $DIR/duplicate_enum_discriminants.rs:3:5 + | +3 | / enum A { +4 | | V1 = 10, +5 | | V2 = 10, +6 | | } + | |_____^ + +error: discriminant value `11` already exists + --> $DIR/duplicate_enum_discriminants.rs:8:5 + | +8 | / enum B { +9 | | V1 = 10, +10 | | V2, +11 | | V3 = 11, +12 | | } + | |_____^ diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs new file mode 100644 index 0000000..a9ad533 --- /dev/null +++ b/tests/ui/empty_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + + } +} + +fn main() {} diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr new file mode 100644 index 0000000..c73578a --- /dev/null +++ b/tests/ui/empty_enum.stderr @@ -0,0 +1,7 @@ +error: enums without any variants are not supported + --> $DIR/empty_enum.rs:3:5 + | +3 | / enum A { +4 | | +5 | | } + | |_____^ diff --git a/tests/ui/enum_match_without_wildcard.rs b/tests/ui/enum_match_without_wildcard.rs new file mode 100644 index 0000000..1a11942 --- /dev/null +++ b/tests/ui/enum_match_without_wildcard.rs @@ -0,0 +1,16 @@ +#[cxx::bridge] +mod ffi { + enum A { + FieldA, + FieldB, + } +} + +fn main() {} + +fn matcher(a: ffi::A) -> u32 { + match a { + ffi::A::FieldA => 2020, + ffi::A::FieldB => 2021, + } +} diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr new file mode 100644 index 0000000..12b8429 --- /dev/null +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -0,0 +1,11 @@ +error[E0004]: non-exhaustive patterns: `A(2u32..=std::u32::MAX)` not covered + --> $DIR/enum_match_without_wildcard.rs:12:11 + | +1 | #[cxx::bridge] + | -------------- `ffi::A` defined here +... +12 | match a { + | ^ pattern `A(2u32..=std::u32::MAX)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `ffi::A` diff --git a/tests/ui/generic_enum.rs b/tests/ui/generic_enum.rs new file mode 100644 index 0000000..808856e --- /dev/null +++ b/tests/ui/generic_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field, + } +} + +fn main() {} diff --git a/tests/ui/generic_enum.stderr b/tests/ui/generic_enum.stderr new file mode 100644 index 0000000..bf267f7 --- /dev/null +++ b/tests/ui/generic_enum.stderr @@ -0,0 +1,5 @@ +error: enums with generic parameters are not allowed + --> $DIR/generic_enum.rs:3:5 + | +3 | enum A { + | ^^^^^^^^^ diff --git a/tests/ui/non_integer_discriminant_enum.rs b/tests/ui/non_integer_discriminant_enum.rs new file mode 100644 index 0000000..388b463 --- /dev/null +++ b/tests/ui/non_integer_discriminant_enum.rs @@ -0,0 +1,8 @@ +#[cxx::bridge] +mod ffi { + enum A { + Field = 2020 + 1, + } +} + +fn main() {} diff --git a/tests/ui/non_integer_discriminant_enum.stderr b/tests/ui/non_integer_discriminant_enum.stderr new file mode 100644 index 0000000..7f70f25 --- /dev/null +++ b/tests/ui/non_integer_discriminant_enum.stderr @@ -0,0 +1,5 @@ +error: enums with non-integer literal discriminants are not supported + --> $DIR/non_integer_discriminant_enum.rs:4:9 + | +4 | Field = 2020 + 1, + | ^^^^^^^^^^^^^^^^ From d7984c2b7f52aa0ded0d991e12b3eaba46bdad86 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:50:10 +0000 Subject: [PATCH 500/2232] Touch up PR 170 --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 788a156..472c079 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -128,16 +128,12 @@ fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let variants = enm.variants.iter().scan(0, |next_discriminant, variant| { - // This span on the pub makes "private type in public interface" errors - // appear in the right place. - let vis = Token![pub](variant.ident.span()); let variant_ident = &variant.ident; - let discriminant = match variant.discriminant { - None => *next_discriminant, - Some(val) => val, - }; + let discriminant = variant.discriminant.unwrap_or(*next_discriminant); *next_discriminant = discriminant + 1; - Some(quote!( #vis const #variant_ident: Self = #ident(#discriminant))) + Some(quote! { + pub const #variant_ident: Self = #ident(#discriminant); + }) }); quote! { #doc @@ -147,7 +143,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { #[allow(non_upper_case_globals)] impl #ident { - #(#variants;)* + #(#variants)* } } } diff --git a/syntax/check.rs b/syntax/check.rs index 321fb1a..7ddcea3 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -202,10 +202,7 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { let mut discriminants = HashSet::new(); enm.variants.iter().fold(0, |next_discriminant, variant| { - let discriminant = match variant.discriminant { - None => next_discriminant, - Some(val) => val, - }; + let discriminant = variant.discriminant.unwrap_or(next_discriminant); if !discriminants.insert(discriminant) { let msg = format!("discriminant value `{}` already exists", discriminant); cx.error(span_for_enum_error(enm), msg); diff --git a/syntax/parse.rs b/syntax/parse.rs index 995b21f..a0dddbd 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -96,8 +96,7 @@ fn parse_enum(item: ItemEnum) -> Result { )); } - let mut doc = Doc::new(); - attrs::parse(&item.attrs, &mut doc, None)?; + let doc = attrs::parse_doc(&item.attrs)?; for variant in &item.variants { match &variant.fields { @@ -105,7 +104,7 @@ fn parse_enum(item: ItemEnum) -> Result { _ => { return Err(Error::new_spanned( variant, - "enums with data are not allowed", + "enums with data are not supported yet", )) } } @@ -135,7 +134,7 @@ fn parse_variant(variant: RustVariant) -> Result { Expr::Lit(ExprLit { lit: Lit::Int(n), .. }), - )) => match n.base10_digits().parse() { + )) => match n.base10_parse() { Ok(val) => Ok(Variant { ident: variant.ident, discriminant: Some(val), @@ -147,7 +146,7 @@ fn parse_variant(variant: RustVariant) -> Result { }, _ => Err(Error::new_spanned( variant, - "enums with non-integer literal discriminants are not supported", + "enums with non-integer literal discriminants are not supported yet", )), } } diff --git a/tests/ui/data_enums.stderr b/tests/ui/data_enums.stderr index 118514e..c78bce5 100644 --- a/tests/ui/data_enums.stderr +++ b/tests/ui/data_enums.stderr @@ -1,4 +1,4 @@ -error: enums with data are not allowed +error: enums with data are not supported yet --> $DIR/data_enums.rs:4:9 | 4 | Field(u64), diff --git a/tests/ui/non_integer_discriminant_enum.stderr b/tests/ui/non_integer_discriminant_enum.stderr index 7f70f25..926fd90 100644 --- a/tests/ui/non_integer_discriminant_enum.stderr +++ b/tests/ui/non_integer_discriminant_enum.stderr @@ -1,4 +1,4 @@ -error: enums with non-integer literal discriminants are not supported +error: enums with non-integer literal discriminants are not supported yet --> $DIR/non_integer_discriminant_enum.rs:4:9 | 4 | Field = 2020 + 1, From 9beba146b77ba2e34656f38a7ed1dcfe698cd598 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:51:50 +0000 Subject: [PATCH 501/2232] Avoid relying on ADL for std::back_inserter --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 71c90b0..b7f8e6d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -237,7 +237,7 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { // requirements for std::iterator_traits. // https://en.cppreference.com/w/cpp/iterator/iterator_traits std::vector cxx_v; - std::copy(v.begin(), v.end(), back_inserter(cxx_v)); + std::copy(v.begin(), v.end(), std::back_inserter(cxx_v)); uint8_t sum = std::accumulate(cxx_v.begin(), cxx_v.end(), 0); if (sum == 200) { cxx_test_suite_set_correct(); From f5aeea2e4c95200cfcbdcf37a49ea45e02b9a55d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:51:51 +0000 Subject: [PATCH 502/2232] Touch up a variable name in vector test --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b7f8e6d..9751969 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -236,9 +236,9 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { // The std::copy() will make sure rust::Vec<>::const_iterator satisfies the // requirements for std::iterator_traits. // https://en.cppreference.com/w/cpp/iterator/iterator_traits - std::vector cxx_v; - std::copy(v.begin(), v.end(), std::back_inserter(cxx_v)); - uint8_t sum = std::accumulate(cxx_v.begin(), cxx_v.end(), 0); + std::vector stdv; + std::copy(v.begin(), v.end(), std::back_inserter(stdv)); + uint8_t sum = std::accumulate(stdv.begin(), stdv.end(), 0); if (sum == 200) { cxx_test_suite_set_correct(); } From 4037de267a30082b67cb9584718ebbabc83d4c0c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:51:51 +0000 Subject: [PATCH 503/2232] Use a better constant string idiom in test --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 9751969..21637d8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -10,7 +10,7 @@ extern "C" bool cxx_test_suite_r_is_correct(const tests::R *) noexcept; namespace tests { -const char *SLICE_DATA = "2020"; +static constexpr char SLICE_DATA[] = "2020"; C::C(size_t n) : n(n) {} @@ -51,7 +51,8 @@ rust::Str c_return_str(const Shared &shared) { rust::Slice c_return_sliceu8(const Shared &shared) { (void)shared; - return rust::Slice(reinterpret_cast(SLICE_DATA), 5); + return rust::Slice(reinterpret_cast(SLICE_DATA), + sizeof(SLICE_DATA)); } rust::String c_return_rust_string() { return "2020"; } @@ -320,8 +321,8 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr(std::unique_ptr(new C{2020})); r_take_ref_c(C{2020}); r_take_str(rust::Str("2020")); - r_take_sliceu8( - rust::Slice(reinterpret_cast(SLICE_DATA), 5)); + r_take_sliceu8(rust::Slice( + reinterpret_cast(SLICE_DATA), sizeof(SLICE_DATA))); r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); From 63f92e8283800e532ea3a4dd017aae73bbf31f34 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:55:23 +0000 Subject: [PATCH 504/2232] Preserve Span of namespace segments --- diff --git a/gen/src/write.rs b/gen/src/write.rs index eb72ba4..8b554d8 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -938,7 +938,7 @@ fn to_typename(namespace: &Namespace, ty: &Type) -> String { Type::Ident(ident) => { let mut path = String::new(); for name in namespace { - path += name; + path += &name.to_string(); path += "::"; } path += &ident.to_string(); @@ -1018,7 +1018,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { let mut inner = String::new(); for name in &out.namespace { - inner += name; + inner += &name.to_string(); inner += "::"; } inner += &ident.to_string(); @@ -1077,7 +1077,7 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { let mut inner = String::new(); for name in &out.namespace { - inner += name; + inner += &name.to_string(); inner += "::"; } inner += &ident.to_string(); diff --git a/syntax/namespace.rs b/syntax/namespace.rs index d26bb9e..5230eb6 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -3,7 +3,7 @@ use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; use syn::parse::{Parse, ParseStream, Result}; -use syn::{Path, Token}; +use syn::{Ident, Path, Token}; mod kw { syn::custom_keyword!(namespace); @@ -11,7 +11,7 @@ mod kw { #[derive(Clone)] pub struct Namespace { - segments: Vec, + segments: Vec, } impl Namespace { @@ -21,7 +21,7 @@ impl Namespace { } } - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter { self.segments.iter() } } @@ -35,7 +35,7 @@ impl Parse for Namespace { let path = input.call(Path::parse_mod_style)?; for segment in path.segments { ident::check(&segment.ident)?; - segments.push(segment.ident.to_string()); + segments.push(segment.ident); } input.parse::>()?; } @@ -46,8 +46,7 @@ impl Parse for Namespace { impl Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for segment in self { - f.write_str(segment)?; - f.write_str("$")?; + write!(f, "{}$", segment)?; } Ok(()) } @@ -60,8 +59,8 @@ impl IdentFragment for Namespace { } impl<'a> IntoIterator for &'a Namespace { - type Item = &'a String; - type IntoIter = Iter<'a, String>; + type Item = &'a Ident; + type IntoIter = Iter<'a, Ident>; fn into_iter(self) -> Self::IntoIter { self.iter() } From 9dcb8339f0b9786872da3bd3c891153074a75ffc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:55:23 +0000 Subject: [PATCH 505/2232] Do not abort parser on namespace ident checks --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 928c6ec..be46533 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -43,10 +43,11 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { match (|| -> Result<_> { let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; + let namespace = bridge.namespace; let apis = syntax::parse_items(bridge.module)?; let types = Types::collect(&apis)?; - check::typecheck(&apis, &types)?; - let out = write::gen(bridge.namespace, &apis, &types, opt, header); + check::typecheck(&namespace, &apis, &types)?; + let out = write::gen(namespace, &apis, &types, opt, header); Ok(out) })() { Ok(out) => out.content(), diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 472c079..2e1873c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -16,7 +16,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { ))?; let apis = syntax::parse_items(content.1)?; let ref types = Types::collect(&apis)?; - check::typecheck(&apis, types)?; + check::typecheck(namespace, &apis, types)?; let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); diff --git a/syntax/check.rs b/syntax/check.rs index 7ddcea3..c2ee1d1 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,4 +1,5 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::namespace::Namespace; use crate::syntax::{ error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, @@ -10,14 +11,16 @@ use std::fmt::Display; use syn::{Error, Result}; struct Check<'a> { + namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Vec, } -pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { +pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> Result<()> { let mut errors = Vec::new(); let mut cx = Check { + namespace, apis, types, errors: &mut errors, @@ -27,6 +30,10 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> { } fn do_typecheck(cx: &mut Check) { + for segment in cx.namespace { + cx.errors.extend(ident::check(segment).err()); + } + for ty in cx.types { match ty { Type::Ident(ident) => check_type_ident(cx, ident), diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 5230eb6..e2dce18 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,4 +1,3 @@ -use crate::syntax::ident; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; @@ -34,7 +33,6 @@ impl Parse for Namespace { input.parse::()?; let path = input.call(Path::parse_mod_style)?; for segment in path.segments { - ident::check(&segment.ident)?; segments.push(segment.ident); } input.parse::>()?; From a83301ce5b751c5dca1b72b8e82f8b4378b2e800 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:55:23 +0000 Subject: [PATCH 506/2232] Update ident checker's error reporting to match type checker's --- diff --git a/syntax/check.rs b/syntax/check.rs index c2ee1d1..33ca5bb 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -10,7 +10,7 @@ use std::collections::HashSet; use std::fmt::Display; use syn::{Error, Result}; -struct Check<'a> { +pub(crate) struct Check<'a> { namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, @@ -31,8 +31,9 @@ pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> R fn do_typecheck(cx: &mut Check) { for segment in cx.namespace { - cx.errors.extend(ident::check(segment).err()); + ident::check(cx, segment); } + ident::check_all(cx, cx.apis); for ty in cx.types { match ty { @@ -65,12 +66,10 @@ fn do_typecheck(cx: &mut Check) { check_multiple_arg_lifetimes(cx, efn); } } - - ident::check_all(cx.apis, cx.errors); } impl Check<'_> { - fn error(&mut self, sp: impl ToTokens, msg: impl Display) { + pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) { self.errors.push(Error::new_spanned(sp, msg)); } } diff --git a/syntax/ident.rs b/syntax/ident.rs index d5bb33f..0694e32 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,41 +1,40 @@ +use crate::syntax::check::Check; use crate::syntax::{error, Api}; use proc_macro2::Ident; -use syn::{Error, Result}; -pub(crate) fn check(ident: &Ident) -> Result<()> { +pub(crate) fn check(cx: &mut Check, ident: &Ident) { let s = ident.to_string(); + if s.starts_with("cxxbridge") { + cx.error(ident, error::CXXBRIDGE_RESERVED.msg); + } if s.contains("__") { - Err(Error::new(ident.span(), error::DOUBLE_UNDERSCORE.msg)) - } else if s.starts_with("cxxbridge") { - Err(Error::new(ident.span(), error::CXXBRIDGE_RESERVED.msg)) - } else { - Ok(()) + cx.error(ident, error::DOUBLE_UNDERSCORE.msg); } } -pub(crate) fn check_all(apis: &[Api], errors: &mut Vec) { +pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) => {} Api::Struct(strct) => { - errors.extend(check(&strct.ident).err()); + check(cx, &strct.ident); for field in &strct.fields { - errors.extend(check(&field.ident).err()); + check(cx, &field.ident); } } Api::Enum(enm) => { - errors.extend(check(&enm.ident).err()); + check(cx, &enm.ident); for variant in &enm.variants { - errors.extend(check(&variant.ident).err()); + check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - errors.extend(check(&ety.ident).err()); + check(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - errors.extend(check(&efn.ident).err()); + check(cx, &efn.ident); for arg in &efn.args { - errors.extend(check(&arg.ident).err()); + check(cx, &arg.ident); } } } From 6b6423edfb3a9e09bfdcc370c7760ead17bdf9c9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:55:23 +0000 Subject: [PATCH 507/2232] Really check everything in check_all --- diff --git a/syntax/check.rs b/syntax/check.rs index 33ca5bb..a99e982 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -30,10 +30,7 @@ pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> R } fn do_typecheck(cx: &mut Check) { - for segment in cx.namespace { - ident::check(cx, segment); - } - ident::check_all(cx, cx.apis); + ident::check_all(cx, cx.namespace, cx.apis); for ty in cx.types { match ty { diff --git a/syntax/ident.rs b/syntax/ident.rs index 0694e32..cec424c 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,8 +1,9 @@ use crate::syntax::check::Check; +use crate::syntax::namespace::Namespace; use crate::syntax::{error, Api}; use proc_macro2::Ident; -pub(crate) fn check(cx: &mut Check, ident: &Ident) { +fn check(cx: &mut Check, ident: &Ident) { let s = ident.to_string(); if s.starts_with("cxxbridge") { cx.error(ident, error::CXXBRIDGE_RESERVED.msg); @@ -12,7 +13,11 @@ pub(crate) fn check(cx: &mut Check, ident: &Ident) { } } -pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { +pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { + for segment in namespace { + check(cx, segment); + } + for api in apis { match api { Api::Include(_) => {} From b3fcf7b7246975eebd030a264bb3021ca983c8f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 05:58:31 +0000 Subject: [PATCH 508/2232] Fix warning about get_unchecked returning a C-incompatible reference out/tests/ffi/lib.rs.cc:1011:22: warning: 'cxxbridge03$std$vector$tests$Shared$get_unchecked' has C-linkage specified, but returns user-defined type 'const tests::Shared &' which is incompatible with C [-Wreturn-type-c-linkage] const tests::Shared &cxxbridge03$std$vector$tests$Shared$get_unchecked(const ::std::vector &s, size_t pos) noexcept { ^ out/tests/ffi/lib.rs.cc:1038:17: warning: 'cxxbridge03$std$vector$tests$C$get_unchecked' has C-linkage specified, but returns user-defined type 'const tests::C &' which is incompatible with C [-Wreturn-type-c-linkage] const tests::C &cxxbridge03$std$vector$tests$C$get_unchecked(const ::std::vector &s, size_t pos) noexcept { ^ 2 warnings generated. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8b554d8..e227ce1 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1232,10 +1232,10 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: writeln!(out, "}}"); writeln!( out, - "const {} &cxxbridge03$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + "const {} *cxxbridge03$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); - writeln!(out, " return s[pos];"); + writeln!(out, " return &s[pos];"); writeln!(out, "}}"); write_unique_ptr_common(out, vector_ty, types); From c4ddb4d172e2d048bd13b2840a8af75e90f2e3f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 17:00:17 +0000 Subject: [PATCH 509/2232] Add tests for by-value-not-supported errors --- diff --git a/syntax/error.rs b/syntax/error.rs index f52d651..f2cb8d1 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -44,7 +44,7 @@ pub static CXX_STRING_BY_VALUE: Error = Error { 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 Box<> or UniquePtr<>"), + note: Some("hint: wrap it in a UniquePtr<>"), }; pub static DOUBLE_UNDERSCORE: Error = Error { diff --git a/tests/ui/by_value_not_supported.rs b/tests/ui/by_value_not_supported.rs new file mode 100644 index 0000000..3ff950a --- /dev/null +++ b/tests/ui/by_value_not_supported.rs @@ -0,0 +1,22 @@ +#[cxx::bridge] +mod ffi { + struct S { + c: C, + r: R, + s: CxxString, + } + + extern "C" { + type C; + } + + extern "Rust" { + type R; + + fn f(c: C) -> C; + fn g(r: R) -> R; + fn h(s: CxxString) -> CxxString; + } +} + +fn main() {} diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr new file mode 100644 index 0000000..1ff8dbf --- /dev/null +++ b/tests/ui/by_value_not_supported.stderr @@ -0,0 +1,53 @@ +error: using C++ type by value is not supported + --> $DIR/by_value_not_supported.rs:4:9 + | +4 | c: C, + | ^^^^ + +error: using opaque Rust type by value is not supported + --> $DIR/by_value_not_supported.rs:5:9 + | +5 | r: R, + | ^^^^ + +error: using C++ string by value is not supported + --> $DIR/by_value_not_supported.rs:6:9 + | +6 | s: CxxString, + | ^^^^^^^^^^^^ + +error: passing C++ type by value is not supported + --> $DIR/by_value_not_supported.rs:16:14 + | +16 | fn f(c: C) -> C; + | ^^^^ + +error: returning C++ type by value is not supported + --> $DIR/by_value_not_supported.rs:16:23 + | +16 | fn f(c: C) -> C; + | ^ + +error: passing opaque Rust type by value is not supported + --> $DIR/by_value_not_supported.rs:17:14 + | +17 | fn g(r: R) -> R; + | ^^^^ + +error: returning opaque Rust type by value is not supported + --> $DIR/by_value_not_supported.rs:17:23 + | +17 | fn g(r: R) -> R; + | ^ + +error: passing C++ string by value is not supported + --> $DIR/by_value_not_supported.rs:18:14 + | +18 | fn h(s: CxxString) -> CxxString; + | ^^^^^^^^^^^^ + +error: returning C++ string by value is not supported + --> $DIR/by_value_not_supported.rs:18:31 + | +18 | fn h(s: CxxString) -> CxxString; + | ^^^^^^^^^ From 04fa0967e2014980c4ae017e5d6cad69fc2934f6 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 01 2020 17:00:31 +0000 Subject: [PATCH 510/2232] Properly handle enum discriminant overflows. This both checks for enum values that are illegal due to overflow and ensures we do not overflow on valid enums. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2e1873c..a3c8d1a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -127,14 +127,19 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; - let variants = enm.variants.iter().scan(0, |next_discriminant, variant| { - let variant_ident = &variant.ident; - let discriminant = variant.discriminant.unwrap_or(*next_discriminant); - *next_discriminant = discriminant + 1; - Some(quote! { - pub const #variant_ident: Self = #ident(#discriminant); - }) - }); + let variants = enm + .variants + .iter() + .scan(None, |prev_discriminant, variant| { + let variant_ident = &variant.ident; + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + *prev_discriminant = Some(discriminant); + Some(quote! { + pub const #variant_ident: Self = #ident(#discriminant); + }) + }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/syntax/check.rs b/syntax/check.rs index a99e982..06bf65b 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -204,14 +204,23 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } let mut discriminants = HashSet::new(); - enm.variants.iter().fold(0, |next_discriminant, variant| { - let discriminant = variant.discriminant.unwrap_or(next_discriminant); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - cx.error(span_for_enum_error(enm), msg); - } - discriminant + 1 - }); + enm.variants + .iter() + .fold(None, |prev_discriminant, variant| { + if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { + let msg = format!("overflowed on value after {}", prev_discriminant.unwrap()); + cx.error(span_for_enum_error(enm), msg); + return None; + } + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + cx.error(span_for_enum_error(enm), msg); + } + Some(discriminant) + }); } fn check_api_type(cx: &mut Check, ty: &ExternType) { diff --git a/tests/ui/enum_overflows.rs b/tests/ui/enum_overflows.rs new file mode 100644 index 0000000..3f351f0 --- /dev/null +++ b/tests/ui/enum_overflows.rs @@ -0,0 +1,17 @@ +#[cxx::bridge] +mod ffi { + enum Good1 { + A = 0xffffffff, + } + enum Good2 { + B = 0xffffffff, + C = 2020, + } + enum Bad { + D = 0xfffffffe, + E, + F, + } +} + +fn main() {} diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr new file mode 100644 index 0000000..3d1b370 --- /dev/null +++ b/tests/ui/enum_overflows.stderr @@ -0,0 +1,9 @@ +error: overflowed on value after 4294967295 + --> $DIR/enum_overflows.rs:10:5 + | +10 | / enum Bad { +11 | | D = 0xfffffffe, +12 | | E, +13 | | F, +14 | | } + | |_____^ From 57d81a12fb3e792be0fd1251868ce9676102b1aa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 17:13:24 +0000 Subject: [PATCH 511/2232] Merge pull request #180 from jgalenson/enums Properly handle enum discriminant overflows. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2e1873c..a3c8d1a 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -127,14 +127,19 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; - let variants = enm.variants.iter().scan(0, |next_discriminant, variant| { - let variant_ident = &variant.ident; - let discriminant = variant.discriminant.unwrap_or(*next_discriminant); - *next_discriminant = discriminant + 1; - Some(quote! { - pub const #variant_ident: Self = #ident(#discriminant); - }) - }); + let variants = enm + .variants + .iter() + .scan(None, |prev_discriminant, variant| { + let variant_ident = &variant.ident; + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + *prev_discriminant = Some(discriminant); + Some(quote! { + pub const #variant_ident: Self = #ident(#discriminant); + }) + }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/syntax/check.rs b/syntax/check.rs index a99e982..06bf65b 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -204,14 +204,23 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } let mut discriminants = HashSet::new(); - enm.variants.iter().fold(0, |next_discriminant, variant| { - let discriminant = variant.discriminant.unwrap_or(next_discriminant); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - cx.error(span_for_enum_error(enm), msg); - } - discriminant + 1 - }); + enm.variants + .iter() + .fold(None, |prev_discriminant, variant| { + if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { + let msg = format!("overflowed on value after {}", prev_discriminant.unwrap()); + cx.error(span_for_enum_error(enm), msg); + return None; + } + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + cx.error(span_for_enum_error(enm), msg); + } + Some(discriminant) + }); } fn check_api_type(cx: &mut Check, ty: &ExternType) { diff --git a/tests/ui/enum_overflows.rs b/tests/ui/enum_overflows.rs new file mode 100644 index 0000000..3f351f0 --- /dev/null +++ b/tests/ui/enum_overflows.rs @@ -0,0 +1,17 @@ +#[cxx::bridge] +mod ffi { + enum Good1 { + A = 0xffffffff, + } + enum Good2 { + B = 0xffffffff, + C = 2020, + } + enum Bad { + D = 0xfffffffe, + E, + F, + } +} + +fn main() {} diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr new file mode 100644 index 0000000..3d1b370 --- /dev/null +++ b/tests/ui/enum_overflows.stderr @@ -0,0 +1,9 @@ +error: overflowed on value after 4294967295 + --> $DIR/enum_overflows.rs:10:5 + | +10 | / enum Bad { +11 | | D = 0xfffffffe, +12 | | E, +13 | | F, +14 | | } + | |_____^ From 8adb223f3e05971c621e92b853841ba699aca419 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 17:14:27 +0000 Subject: [PATCH 512/2232] Restore support for rustc 1.42 --- diff --git a/syntax/check.rs b/syntax/check.rs index 06bf65b..0ea53df 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -8,6 +8,7 @@ use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::collections::HashSet; use std::fmt::Display; +use std::u32; use syn::{Error, Result}; pub(crate) struct Check<'a> { From 1ba09e1422394e1869f26fad34e93c31ab899782 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 17:14:41 +0000 Subject: [PATCH 513/2232] Sort errors reported by line number --- diff --git a/gen/src/error.rs b/gen/src/error.rs index 2e8ecc4..b91c166 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -57,6 +57,7 @@ impl From for Error { pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { match error { Error::Syn(syn_error) => { + let syn_error = sort_syn_errors(syn_error); let writer = StandardStream::stderr(ColorChoice::Auto); let ref mut stderr = writer.lock(); for error in syn_error { @@ -69,6 +70,15 @@ pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { process::exit(1); } +fn sort_syn_errors(error: syn::Error) -> Vec { + let mut errors: Vec<_> = error.into_iter().collect(); + errors.sort_by_key(|e| { + let start = e.span().start(); + (start.line, start.column) + }); + errors +} + fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { let span = error.span(); let start = span.start(); From e720e852c1733cca64e0abcae36bd56d5bc92799 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 17:35:45 +0000 Subject: [PATCH 514/2232] Merge pull request #181 from dtolnay/sort Sort errors reported by line number --- diff --git a/gen/src/error.rs b/gen/src/error.rs index 2e8ecc4..b91c166 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -57,6 +57,7 @@ impl From for Error { pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { match error { Error::Syn(syn_error) => { + let syn_error = sort_syn_errors(syn_error); let writer = StandardStream::stderr(ColorChoice::Auto); let ref mut stderr = writer.lock(); for error in syn_error { @@ -69,6 +70,15 @@ pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { process::exit(1); } +fn sort_syn_errors(error: syn::Error) -> Vec { + let mut errors: Vec<_> = error.into_iter().collect(); + errors.sort_by_key(|e| { + let start = e.span().start(); + (start.line, start.column) + }); + errors +} + fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) { let span = error.span(); let start = span.start(); From db1ec312835f0aa8929660622360db2b322d0614 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 01 2020 21:00:48 +0000 Subject: [PATCH 515/2232] Expose the enum value to Rust as a field --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index a3c8d1a..19cde4f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -137,14 +137,16 @@ fn expand_enum(enm: &Enum) -> TokenStream { .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); *prev_discriminant = Some(discriminant); Some(quote! { - pub const #variant_ident: Self = #ident(#discriminant); + pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] - pub struct #ident(u32); + pub struct #ident { + pub repr: u32, + } #[allow(non_upper_case_globals)] impl #ident { diff --git a/tests/test.rs b/tests/test.rs index 1156d5d..b8593c5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -52,15 +52,15 @@ fn test_c_return() { assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { - ffi::Enum::AVal => {} + enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr), _ => assert!(false), } match ffi::c_return_enum(1) { - ffi::Enum::BVal => {} + enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr), _ => assert!(false), } match ffi::c_return_enum(2021) { - ffi::Enum::CVal => {} + enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr), _ => assert!(false), } } @@ -160,6 +160,13 @@ fn test_c_method_calls() { assert_eq!(old_value, unique_ptr.get2()) } +#[test] +fn test_enum_representations() { + assert_eq!(0, ffi::Enum::AVal.repr); + assert_eq!(2020, ffi::Enum::BVal.repr); + assert_eq!(2021, ffi::Enum::CVal.repr); +} + #[no_mangle] extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { Box::into_raw(Box::new(2020usize)) diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 12b8429..93a8957 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A(2u32..=std::u32::MAX)` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2u32..=std::u32::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A(2u32..=std::u32::MAX)` not covered + | ^ pattern `A { repr: 2u32..=std::u32::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` From 18cd7572131a0bf86d23eeca40ad436b7c6f35ed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 21:26:15 +0000 Subject: [PATCH 516/2232] Merge pull request #182 from jgalenson/enums Expose the enum value to Rust as a field --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index a3c8d1a..19cde4f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -137,14 +137,16 @@ fn expand_enum(enm: &Enum) -> TokenStream { .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); *prev_discriminant = Some(discriminant); Some(quote! { - pub const #variant_ident: Self = #ident(#discriminant); + pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] - pub struct #ident(u32); + pub struct #ident { + pub repr: u32, + } #[allow(non_upper_case_globals)] impl #ident { diff --git a/tests/test.rs b/tests/test.rs index 1156d5d..b8593c5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -52,15 +52,15 @@ fn test_c_return() { assert_eq!(2020, ffi::c_return_identity(2020)); assert_eq!(2021, ffi::c_return_sum(2020, 1)); match ffi::c_return_enum(0) { - ffi::Enum::AVal => {} + enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr), _ => assert!(false), } match ffi::c_return_enum(1) { - ffi::Enum::BVal => {} + enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr), _ => assert!(false), } match ffi::c_return_enum(2021) { - ffi::Enum::CVal => {} + enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr), _ => assert!(false), } } @@ -160,6 +160,13 @@ fn test_c_method_calls() { assert_eq!(old_value, unique_ptr.get2()) } +#[test] +fn test_enum_representations() { + assert_eq!(0, ffi::Enum::AVal.repr); + assert_eq!(2020, ffi::Enum::BVal.repr); + assert_eq!(2021, ffi::Enum::CVal.repr); +} + #[no_mangle] extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { Box::into_raw(Box::new(2020usize)) diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 12b8429..93a8957 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A(2u32..=std::u32::MAX)` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2u32..=std::u32::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A(2u32..=std::u32::MAX)` not covered + | ^ pattern `A { repr: 2u32..=std::u32::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` From 761a5fc7fdafc7cc8d52c7f68f843b3facc00478 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 23:02:02 +0000 Subject: [PATCH 517/2232] Support building C++ code generator with panic=abort --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 869f543..548dc85 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -14,7 +14,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } +proc-macro2 = { version = "1.0.11", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 9927d3f..20fa12e 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } +proc-macro2 = { version = "1.0.11", features = ["span-locations"] } quote = "1.0" structopt = "0.3" syn = { version = "1.0", features = ["full"] } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index be46533..58a78b1 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -41,6 +41,7 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { Err(err) => format_err(path, "", Error::Io(err)), }; match (|| -> Result<_> { + proc_macro2::fallback::force(); let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; let namespace = bridge.namespace; diff --git a/third-party/BUCK b/third-party/BUCK index 3500cb0..bf772cf 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -99,7 +99,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.3/src/**"]), + srcs = glob(["vendor/quote-1.0.4/src/**"]), visibility = ["PUBLIC"], features = ["proc-macro"], deps = [":proc-macro2"], diff --git a/third-party/BUILD b/third-party/BUILD index b6605e4..140fec8 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -104,7 +104,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.3/src/**"]), + srcs = glob(["vendor/quote-1.0.4/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], deps = [":proc-macro2"], diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f4e5903..b79fae2 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -211,18 +211,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "9dd1c38e7b0b6b61bcfbdc08c801f3c3066e884bd8e6764bdd2e5971da1d7c4d" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" dependencies = [ "proc-macro2", ] From 7bdd9b51db743ca487f0dbd8681cd5a3b15d69f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 01 2020 23:16:23 +0000 Subject: [PATCH 518/2232] Merge pull request #184 from dtolnay/fallback Support building C++ code generator with panic=abort --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 869f543..548dc85 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -14,7 +14,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } +proc-macro2 = { version = "1.0.11", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 9927d3f..20fa12e 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0", features = ["span-locations"] } +proc-macro2 = { version = "1.0.11", features = ["span-locations"] } quote = "1.0" structopt = "0.3" syn = { version = "1.0", features = ["full"] } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index be46533..58a78b1 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -41,6 +41,7 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { Err(err) => format_err(path, "", Error::Io(err)), }; match (|| -> Result<_> { + proc_macro2::fallback::force(); let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; let namespace = bridge.namespace; diff --git a/third-party/BUCK b/third-party/BUCK index 3500cb0..bf772cf 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -99,7 +99,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.3/src/**"]), + srcs = glob(["vendor/quote-1.0.4/src/**"]), visibility = ["PUBLIC"], features = ["proc-macro"], deps = [":proc-macro2"], diff --git a/third-party/BUILD b/third-party/BUILD index b6605e4..140fec8 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.10/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -104,7 +104,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.3/src/**"]), + srcs = glob(["vendor/quote-1.0.4/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], deps = [":proc-macro2"], diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f4e5903..b79fae2 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -211,18 +211,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "9dd1c38e7b0b6b61bcfbdc08c801f3c3066e884bd8e6764bdd2e5971da1d7c4d" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" dependencies = [ "proc-macro2", ] From b3f66461cd849f8a0451d42ef25614691e4871d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 02 2020 00:29:12 +0000 Subject: [PATCH 519/2232] Update past yanked version of proc-macro2 1.0.11 was yanked. --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 548dc85..f120099 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -14,7 +14,7 @@ travis-ci = { repository = "dtolnay/cxx" } anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.11", features = ["span-locations"] } +proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 20fa12e..99bff36 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ travis-ci = { repository = "dtolnay/cxx" } [dependencies] anyhow = "1.0" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.11", features = ["span-locations"] } +proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" structopt = "0.3" syn = { version = "1.0", features = ["full"] } diff --git a/third-party/BUCK b/third-party/BUCK index bf772cf..2de784b 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.12/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", diff --git a/third-party/BUILD b/third-party/BUILD index 140fec8..4d8fd9f 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.11/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.12/src/**"]), crate_features = [ "proc-macro", "span-locations", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b79fae2..3397192 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd1c38e7b0b6b61bcfbdc08c801f3c3066e884bd8e6764bdd2e5971da1d7c4d" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" dependencies = [ "unicode-xid", ] From 0d366c754bbca757a229186dff511796425f5617 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 02 2020 03:51:38 +0000 Subject: [PATCH 520/2232] Remove CI badge from Cargo.toml Support for badges has been deprecated by crates.io. --- diff --git a/Cargo.toml b/Cargo.toml index f5e5b9f..1455e55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,6 @@ documentation = "https://docs.rs/cxx" readme = "README.md" exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] -[badges] -travis-ci = { repository = "dtolnay/cxx" } - [dependencies] cxxbridge-macro = { version = "=0.3.0", path = "macro" } link-cplusplus = "1.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index f120099..1cbcced 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -7,9 +7,6 @@ license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a Cargo build." repository = "https://github.com/dtolnay/cxx" -[badges] -travis-ci = { repository = "dtolnay/cxx" } - [dependencies] anyhow = "1.0" cc = "1.0.49" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 99bff36..57f2e39 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -11,9 +11,6 @@ repository = "https://github.com/dtolnay/cxx" name = "cxxbridge" path = "src/main.rs" -[badges] -travis-ci = { repository = "dtolnay/cxx" } - [dependencies] anyhow = "1.0" codespan-reporting = "0.9" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 51fba71..4c2d423 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -11,9 +11,6 @@ exclude = ["README.md"] [lib] proc-macro = true -[badges] -travis-ci = { repository = "dtolnay/cxx" } - [dependencies] proc-macro2 = "1.0" quote = "1.0" From 193ce8171ad946650b3c488d5d8b2cd25a965314 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 03 2020 04:20:14 +0000 Subject: [PATCH 521/2232] Enable GitHub Actions --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..06cedb4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: test + +on: + push: + pull_request: + schedule: [cron: "40 1 * * *"] + +jobs: + test: + name: ${{matrix.name || format('Rust {0}', matrix.rust)}} + runs-on: ${{matrix.os || 'ubuntu'}}-latest + strategy: + fail-fast: false + matrix: + include: + - rust: nightly + - rust: beta + - rust: stable + - rust: 1.43.0 + - name: macOS + rust: nightly + os: macos + - name: Windows (gnu) + rust: nightly-x86_64-pc-windows-gnu + os: windows + - name: Windows (msvc) + rust: nightly-x86_64-pc-windows-msvc + os: windows + steps: + - name: Enable symlinks (windows) + if: matrix.os == 'windows' + run: git config --global core.symlinks true + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo test + + msrv: + name: Rust 1.42.0 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@1.42.0 + - run: cargo run --manifest-path demo-rs/Cargo.toml + + buck: + name: Buck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-java@v1 + with: + java-version: 8 + java-package: jre + - name: Install Buck + run: | + mkdir bin + wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/v2019.10.17.01/buck-v2019.10.17.01.pex + chmod +x bin/buck + echo ::add-path::bin + - name: Vendor dependencies + run: | + cp third-party/Cargo.lock . + cargo vendor --versioned-dirs --locked third-party/vendor + - run: buck build :cxx#check --verbose=0 + - run: buck run demo-rs --verbose=0 + - run: buck test ... --verbose=0 + + bazel: + name: Bazel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Bazel + run: | + wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh + chmod +x install.sh + ./install.sh --user + - name: Vendor dependencies + run: | + cp third-party/Cargo.lock . + cargo vendor --versioned-dirs --locked third-party/vendor + - run: bazel run demo-rs --verbose_failures --noshow_progress + - run: bazel test ... --verbose_failures --noshow_progress From 2c61f66ecd5a314db6716f9e32db576df6b2ae8e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 03 2020 04:34:42 +0000 Subject: [PATCH 522/2232] Merge pull request #185 from dtolnay/actions Enable GitHub Actions --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..06cedb4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: test + +on: + push: + pull_request: + schedule: [cron: "40 1 * * *"] + +jobs: + test: + name: ${{matrix.name || format('Rust {0}', matrix.rust)}} + runs-on: ${{matrix.os || 'ubuntu'}}-latest + strategy: + fail-fast: false + matrix: + include: + - rust: nightly + - rust: beta + - rust: stable + - rust: 1.43.0 + - name: macOS + rust: nightly + os: macos + - name: Windows (gnu) + rust: nightly-x86_64-pc-windows-gnu + os: windows + - name: Windows (msvc) + rust: nightly-x86_64-pc-windows-msvc + os: windows + steps: + - name: Enable symlinks (windows) + if: matrix.os == 'windows' + run: git config --global core.symlinks true + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo test + + msrv: + name: Rust 1.42.0 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@1.42.0 + - run: cargo run --manifest-path demo-rs/Cargo.toml + + buck: + name: Buck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-java@v1 + with: + java-version: 8 + java-package: jre + - name: Install Buck + run: | + mkdir bin + wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/v2019.10.17.01/buck-v2019.10.17.01.pex + chmod +x bin/buck + echo ::add-path::bin + - name: Vendor dependencies + run: | + cp third-party/Cargo.lock . + cargo vendor --versioned-dirs --locked third-party/vendor + - run: buck build :cxx#check --verbose=0 + - run: buck run demo-rs --verbose=0 + - run: buck test ... --verbose=0 + + bazel: + name: Bazel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Bazel + run: | + wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh + chmod +x install.sh + ./install.sh --user + - name: Vendor dependencies + run: | + cp third-party/Cargo.lock . + cargo vendor --versioned-dirs --locked third-party/vendor + - run: bazel run demo-rs --verbose_failures --noshow_progress + - run: bazel test ... --verbose_failures --noshow_progress From 96a826b8f09f4064642aaca2d125a9d2e4554267 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:17:12 +0000 Subject: [PATCH 523/2232] Check function signature restrictions in a more appropriate place --- diff --git a/syntax/check.rs b/syntax/check.rs index 0ea53df..5d442ce 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -55,15 +55,6 @@ fn do_typecheck(cx: &mut Check) { _ => {} } } - - for api in cx.apis { - if let Api::CxxFunction(efn) = api { - check_mut_return_restriction(cx, efn); - } - if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api { - check_multiple_arg_lifetimes(cx, efn); - } - } } impl Check<'_> { @@ -282,6 +273,12 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { cx.error(ty, "returning a function pointer is not implemented yet"); } } + + if efn.lang == Lang::Cxx { + check_mut_return_restriction(cx, efn); + } + + check_multiple_arg_lifetimes(cx, efn); } fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { From ab91445c1dcc617219f130e60da480ea4f015ba2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:31:28 +0000 Subject: [PATCH 524/2232] Check type name duplication more compactly --- diff --git a/syntax/set.rs b/syntax/set.rs index ca816cf..688d1c0 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -18,10 +18,12 @@ where } } - pub fn insert(&mut self, value: &'a T) { - if self.set.insert(value) { + 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(&self, value: &T) -> bool { diff --git a/syntax/types.rs b/syntax/types.rs index bb5a843..11db1b7 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -3,7 +3,7 @@ use crate::syntax::set::OrderedSet as Set; use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type}; use proc_macro2::Ident; use quote::quote; -use std::collections::BTreeMap as Map; +use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; use syn::{Error, Result}; pub struct Types<'a> { @@ -43,16 +43,12 @@ impl<'a> Types<'a> { } } + let mut type_names = UnorderedSet::new(); for api in apis { match api { Api::Include(_) => {} Api::Struct(strct) => { - let ident = &strct.ident; - if structs.contains_key(ident) - || enums.contains_key(ident) - || cxx.contains(ident) - || rust.contains(ident) - { + if !type_names.insert(&strct.ident) { return Err(duplicate_struct(strct)); } structs.insert(strct.ident.clone(), strct); @@ -61,37 +57,22 @@ impl<'a> Types<'a> { } } Api::Enum(enm) => { - let ident = &enm.ident; - if structs.contains_key(ident) - || enums.contains_key(ident) - || cxx.contains(ident) - || rust.contains(ident) - { + if !type_names.insert(&enm.ident) { return Err(duplicate_enum(enm)); } enums.insert(enm.ident.clone(), enm); } Api::CxxType(ety) => { - let ident = &ety.ident; - if structs.contains_key(ident) - || enums.contains_key(ident) - || cxx.contains(ident) - || rust.contains(ident) - { + if !type_names.insert(&ety.ident) { return Err(duplicate_type(ety)); } - cxx.insert(ident); + cxx.insert(&ety.ident); } Api::RustType(ety) => { - let ident = &ety.ident; - if structs.contains_key(ident) - || enums.contains_key(ident) - || cxx.contains(ident) - || rust.contains(ident) - { + if !type_names.insert(&ety.ident) { return Err(duplicate_type(ety)); } - rust.insert(ident); + rust.insert(&ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { for arg in &efn.args { From d932041745a3cd9fd314c4bc266ba1cae2532bcf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:31:56 +0000 Subject: [PATCH 525/2232] Catch function name collisions --- diff --git a/syntax/types.rs b/syntax/types.rs index 11db1b7..e09b5cf 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type}; use proc_macro2::Ident; use quote::quote; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -44,6 +44,7 @@ impl<'a> Types<'a> { } let mut type_names = UnorderedSet::new(); + let mut function_names = UnorderedSet::new(); for api in apis { match api { Api::Include(_) => {} @@ -75,6 +76,9 @@ impl<'a> Types<'a> { rust.insert(&ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { + if !function_names.insert((&efn.receiver, &efn.ident)) { + return Err(duplicate_function(efn)); + } for arg in &efn.args { visit(&mut all, &arg.ty); } @@ -146,3 +150,7 @@ fn duplicate_type(ety: &ExternType) -> Error { let range = quote!(#type_token #ident); Error::new_spanned(range, "duplicate type") } + +fn duplicate_function(efn: &ExternFn) -> Error { + Error::new_spanned(efn, "duplicate function") +} From 83496ebf335d1df433b5047902478af7ae89411d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:36:53 +0000 Subject: [PATCH 526/2232] Move error reporting span computation to tokens.rs --- diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 13cbfcf..934c85b 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,5 +1,7 @@ use crate::syntax::atom::Atom::*; -use crate::syntax::{Derive, ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type, Var}; +use crate::syntax::{ + Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, Var, +}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use syn::Token; @@ -76,6 +78,30 @@ impl ToTokens for Derive { } } +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.ident.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.ident.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.ident.to_tokens(tokens); + } +} + impl ToTokens for ExternFn { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/syntax/types.rs b/syntax/types.rs index e09b5cf..f0ef3a8 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -2,7 +2,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::set::OrderedSet as Set; use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type}; use proc_macro2::Ident; -use quote::quote; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; use syn::{Error, Result}; @@ -131,24 +130,15 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } fn duplicate_struct(strct: &Struct) -> Error { - let struct_token = strct.struct_token; - let ident = &strct.ident; - let range = quote!(#struct_token #ident); - Error::new_spanned(range, "duplicate type") + Error::new_spanned(strct, "duplicate type") } fn duplicate_enum(enm: &Enum) -> Error { - let enum_token = enm.enum_token; - let ident = &enm.ident; - let range = quote!(#enum_token #ident); - Error::new_spanned(range, "duplicate type") + Error::new_spanned(enm, "duplicate type") } fn duplicate_type(ety: &ExternType) -> Error { - let type_token = ety.type_token; - let ident = &ety.ident; - let range = quote!(#type_token #ident); - Error::new_spanned(range, "duplicate type") + Error::new_spanned(ety, "duplicate type") } fn duplicate_function(efn: &ExternFn) -> Error { From 7bc2edd7340d72de67af83729f23c3245e9654cf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:41:53 +0000 Subject: [PATCH 527/2232] Include conflicting name in duplicate name error message --- diff --git a/syntax/types.rs b/syntax/types.rs index f0ef3a8..4842507 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,8 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, Struct, Type}; use proc_macro2::Ident; +use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; use syn::{Error, Result}; @@ -48,35 +49,40 @@ impl<'a> Types<'a> { match api { Api::Include(_) => {} Api::Struct(strct) => { - if !type_names.insert(&strct.ident) { - return Err(duplicate_struct(strct)); + let ident = &strct.ident; + if !type_names.insert(ident) { + return Err(duplicate_name(strct, ident)); } - structs.insert(strct.ident.clone(), strct); + structs.insert(ident.clone(), strct); for field in &strct.fields { visit(&mut all, &field.ty); } } Api::Enum(enm) => { - if !type_names.insert(&enm.ident) { - return Err(duplicate_enum(enm)); + let ident = &enm.ident; + if !type_names.insert(ident) { + return Err(duplicate_name(enm, ident)); } - enums.insert(enm.ident.clone(), enm); + enums.insert(ident.clone(), enm); } Api::CxxType(ety) => { - if !type_names.insert(&ety.ident) { - return Err(duplicate_type(ety)); + let ident = &ety.ident; + if !type_names.insert(ident) { + return Err(duplicate_name(ety, ident)); } - cxx.insert(&ety.ident); + cxx.insert(ident); } Api::RustType(ety) => { - if !type_names.insert(&ety.ident) { - return Err(duplicate_type(ety)); + let ident = &ety.ident; + if !type_names.insert(ident) { + return Err(duplicate_name(ety, ident)); } - rust.insert(&ety.ident); + rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - if !function_names.insert((&efn.receiver, &efn.ident)) { - return Err(duplicate_function(efn)); + let ident = &efn.ident; + if !function_names.insert((&efn.receiver, ident)) { + return Err(duplicate_name(efn, ident)); } for arg in &efn.args { visit(&mut all, &arg.ty); @@ -129,18 +135,7 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } } -fn duplicate_struct(strct: &Struct) -> Error { - Error::new_spanned(strct, "duplicate type") -} - -fn duplicate_enum(enm: &Enum) -> Error { - Error::new_spanned(enm, "duplicate type") -} - -fn duplicate_type(ety: &ExternType) -> Error { - Error::new_spanned(ety, "duplicate type") -} - -fn duplicate_function(efn: &ExternFn) -> Error { - Error::new_spanned(efn, "duplicate function") +fn duplicate_name(sp: impl ToTokens, ident: &Ident) -> Error { + let msg = format!("the name `{}` is defined multiple times", ident); + Error::new_spanned(sp, msg) } From 2ec14632c091bcb459b83b953f465a96c5030ff9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 07:53:12 +0000 Subject: [PATCH 528/2232] Be consistent about certain variable names always meaning a reference --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 58a78b1..c7b3c3c 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -44,11 +44,11 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { proc_macro2::fallback::force(); let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; - let namespace = bridge.namespace; - let apis = syntax::parse_items(bridge.module)?; - let types = Types::collect(&apis)?; - check::typecheck(&namespace, &apis, &types)?; - let out = write::gen(namespace, &apis, &types, opt, header); + let ref namespace = bridge.namespace; + let ref apis = syntax::parse_items(bridge.module)?; + let ref types = Types::collect(apis)?; + check::typecheck(namespace, apis, types)?; + let out = write::gen(namespace, apis, types, opt, header); Ok(out) })() { Ok(out) => out.content(), diff --git a/gen/src/write.rs b/gen/src/write.rs index e227ce1..8ce3813 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -8,7 +8,7 @@ use proc_macro2::Ident; use std::collections::HashMap; pub(super) fn gen( - namespace: Namespace, + namespace: &Namespace, apis: &[Api], types: &Types, opt: Opt, @@ -32,7 +32,7 @@ pub(super) fn gen( write_include_cxxbridge(out, apis, types); out.next_section(); - for name in &namespace { + for name in namespace { writeln!(out, "namespace {} {{", name); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 19cde4f..737c389 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -14,14 +14,14 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { Span::call_site(), "#[cxx::bridge] module must have inline contents", ))?; - let apis = syntax::parse_items(content.1)?; - let ref types = Types::collect(&apis)?; - check::typecheck(namespace, &apis, types)?; + let ref apis = syntax::parse_items(content.1)?; + let ref types = Types::collect(apis)?; + check::typecheck(namespace, apis, types)?; let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - for api in &apis { + for api in apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); let ident = &ety.ident; @@ -32,7 +32,7 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { } } - for api in &apis { + for api in apis { match api { Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), From 15a6c763522593484c5d8847bf884eb53afb4313 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:29:01 +0000 Subject: [PATCH 529/2232] Add test of invocation with multiple parse errors --- diff --git a/tests/ui/multiple_parse_error.rs b/tests/ui/multiple_parse_error.rs new file mode 100644 index 0000000..061eab6 --- /dev/null +++ b/tests/ui/multiple_parse_error.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + struct Monad; + + extern "Haskell" { + } +} + +fn main() {} diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr new file mode 100644 index 0000000..61c4fba --- /dev/null +++ b/tests/ui/multiple_parse_error.stderr @@ -0,0 +1,5 @@ +error: struct with generic parameters is not supported yet + --> $DIR/multiple_parse_error.rs:3:5 + | +3 | struct Monad; + | ^^^^^^^^^^^^^^^ From df344a82d91b25ef7fd1461482dc2bc35318294d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:29:16 +0000 Subject: [PATCH 530/2232] Extract error collection to be not specific to check.rs --- diff --git a/syntax/check.rs b/syntax/check.rs index 5d442ce..3fb0d06 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::{ error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, @@ -9,17 +10,17 @@ use quote::{quote, ToTokens}; use std::collections::HashSet; use std::fmt::Display; use std::u32; -use syn::{Error, Result}; +use syn::Result; pub(crate) struct Check<'a> { namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, - errors: &'a mut Vec, + errors: &'a mut Errors, } pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> Result<()> { - let mut errors = Vec::new(); + let mut errors = Errors::new(); let mut cx = Check { namespace, apis, @@ -27,7 +28,7 @@ pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> R errors: &mut errors, }; do_typecheck(&mut cx); - combine_errors(errors) + errors.propagate() } fn do_typecheck(cx: &mut Check) { @@ -59,7 +60,7 @@ fn do_typecheck(cx: &mut Check) { impl Check<'_> { pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) { - self.errors.push(Error::new_spanned(sp, msg)); + self.errors.error(sp, msg); } } @@ -373,18 +374,6 @@ fn span_for_receiver_error(receiver: &Receiver) -> TokenStream { } } -fn combine_errors(errors: Vec) -> Result<()> { - let mut iter = errors.into_iter(); - let mut all_errors = match iter.next() { - Some(err) => err, - None => return Ok(()), - }; - for err in iter { - all_errors.combine(err); - } - Err(all_errors) -} - fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { diff --git a/syntax/mod.rs b/syntax/mod.rs index 9b1a7e5..e6c5bb8 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -10,6 +10,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; +pub mod report; pub mod set; pub mod symbol; mod tokens; diff --git a/syntax/report.rs b/syntax/report.rs new file mode 100644 index 0000000..04f21d8 --- /dev/null +++ b/syntax/report.rs @@ -0,0 +1,29 @@ +use quote::ToTokens; +use std::fmt::Display; +use syn::{Error, Result}; + +pub struct Errors { + errors: Vec, +} + +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 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) + } +} From 0dd85ff5f734a1f6eaff658640b75a47518fc26b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:29:17 +0000 Subject: [PATCH 531/2232] Move error collection one level out of type checker --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index c7b3c3c..0cfdc59 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -8,6 +8,7 @@ mod write; use self::error::{format_err, Error, Result}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use quote::quote; use std::fs; @@ -42,12 +43,14 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { }; match (|| -> Result<_> { proc_macro2::fallback::force(); + let ref mut errors = Errors::new(); let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; let ref apis = syntax::parse_items(bridge.module)?; let ref types = Types::collect(apis)?; - check::typecheck(namespace, apis, types)?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; let out = write::gen(namespace, apis, types, opt, header); Ok(out) })() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 737c389..f5cbbf2 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, @@ -9,6 +10,7 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { + let ref mut errors = Errors::new(); let ident = &ffi.ident; let content = ffi.content.ok_or(Error::new( Span::call_site(), @@ -16,7 +18,8 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { ))?; let ref apis = syntax::parse_items(content.1)?; let ref types = Types::collect(apis)?; - check::typecheck(namespace, apis, types)?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); diff --git a/syntax/check.rs b/syntax/check.rs index 3fb0d06..72b4a6c 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -10,7 +10,6 @@ use quote::{quote, ToTokens}; use std::collections::HashSet; use std::fmt::Display; use std::u32; -use syn::Result; pub(crate) struct Check<'a> { namespace: &'a Namespace, @@ -19,16 +18,13 @@ pub(crate) struct Check<'a> { errors: &'a mut Errors, } -pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> Result<()> { - let mut errors = Errors::new(); - let mut cx = Check { +pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { + do_typecheck(&mut Check { namespace, apis, types, - errors: &mut errors, - }; - do_typecheck(&mut cx); - errors.propagate() + errors: cx, + }); } fn do_typecheck(cx: &mut Check) { From cbf3f03ff24ab357ae9beb77cf98809a0b721767 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:29:17 +0000 Subject: [PATCH 532/2232] Demarcate the infallible majority of expand.rs --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index f5cbbf2..d1af2cf 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -9,10 +9,9 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse_quote, Error, ItemMod, Result, Token}; -pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { +pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { let ref mut errors = Errors::new(); - let ident = &ffi.ident; - let content = ffi.content.ok_or(Error::new( + let content = ffi.content.take().ok_or(Error::new( Span::call_site(), "#[cxx::bridge] module must have inline contents", ))?; @@ -21,6 +20,10 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { check::typecheck(errors, namespace, apis, types); errors.propagate()?; + Ok(expand(namespace, ffi, apis, types)) +} + +fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); @@ -96,15 +99,16 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { .into_iter() .filter(|attr| attr.path.is_ident("doc")); let vis = &ffi.vis; + let ident = &ffi.ident; - Ok(quote! { + quote! { #(#attrs)* #[deny(improper_ctypes)] #[allow(non_snake_case)] #vis mod #ident { #expanded } - }) + } } fn expand_struct(strct: &Struct) -> TokenStream { From 5275978d1390846b3b346b0dc0a2d668ffa44e23 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:29:17 +0000 Subject: [PATCH 533/2232] Batch errors from the parser --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 0cfdc59..6663e4b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -47,8 +47,9 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(bridge.module)?; - let ref types = Types::collect(apis)?; + let ref apis = syntax::parse_items(errors, bridge.module); + let ref types = Types::collect(errors, apis); + errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; let out = write::gen(namespace, apis, types, opt, header); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d1af2cf..e67902b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -15,8 +15,9 @@ pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { Span::call_site(), "#[cxx::bridge] module must have inline contents", ))?; - let ref apis = syntax::parse_items(content.1)?; - let ref types = Types::collect(apis)?; + let ref apis = syntax::parse_items(errors, content.1); + let ref types = Types::collect(errors, apis); + errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; diff --git a/syntax/parse.rs b/syntax/parse.rs index a0dddbd..8bf0a4a 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,3 +1,4 @@ +use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, @@ -16,27 +17,24 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(items: Vec) -> Result> { +pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => { - let strct = parse_struct(item)?; - apis.push(strct); - } - Item::Enum(item) => { - let enm = parse_enum(item)?; - apis.push(enm); - } - Item::ForeignMod(foreign_mod) => { - let functions = parse_foreign_mod(foreign_mod)?; - apis.extend(functions); - } - Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)), - _ => return Err(Error::new_spanned(item, "unsupported item")), + Item::Struct(item) => match parse_struct(item) { + Ok(strct) => apis.push(strct), + Err(err) => cx.push(err), + }, + Item::Enum(item) => match parse_enum(item) { + Ok(enm) => apis.push(enm), + Err(err) => cx.push(err), + }, + Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), + Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), + _ => cx.error(item, "unsupported item"), } } - Ok(apis) + apis } fn parse_struct(item: ItemStruct) -> Result { @@ -151,8 +149,11 @@ fn parse_variant(variant: RustVariant) -> Result { } } -fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { - let lang = parse_lang(foreign_mod.abi)?; +fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { + let lang = match parse_lang(foreign_mod.abi) { + Ok(lang) => lang, + Err(err) => return cx.push(err), + }; let api_type = match lang { Lang::Cxx => Api::CxxType, Lang::Rust => Api::RustType, @@ -165,19 +166,21 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => { - let ety = parse_extern_type(foreign)?; - items.push(api_type(ety)); - } - ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang)?; - items.push(api_function(efn)); - } + ForeignItem::Type(foreign) => match parse_extern_type(foreign) { + Ok(ety) => items.push(api_type(ety)), + Err(err) => cx.push(err), + }, + ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) { + Ok(efn) => items.push(api_function(efn)), + Err(err) => cx.push(err), + }, ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { - let include = foreign.mac.parse_body()?; - items.push(Api::Include(include)); + match foreign.mac.parse_body() { + Ok(include) => items.push(Api::Include(include)), + Err(err) => cx.push(err), + } } - _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), + _ => cx.error(foreign, "unsupported foreign item"), } } @@ -198,7 +201,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { } } - Ok(items) + out.extend(items); } fn parse_lang(abi: Abi) -> Result { diff --git a/syntax/report.rs b/syntax/report.rs index 04f21d8..d1d8bc9 100644 --- a/syntax/report.rs +++ b/syntax/report.rs @@ -15,6 +15,10 @@ impl Errors { 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() { diff --git a/syntax/types.rs b/syntax/types.rs index 4842507..02249e2 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,10 +1,10 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; use crate::syntax::{Api, Derive, Enum, Struct, Type}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; -use syn::{Error, Result}; pub struct Types<'a> { pub all: Set<'a, Type>, @@ -15,7 +15,7 @@ pub struct Types<'a> { } impl<'a> Types<'a> { - pub fn collect(apis: &'a [Api]) -> Result { + pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self { let mut all = Set::new(); let mut structs = Map::new(); let mut enums = Map::new(); @@ -50,39 +50,41 @@ impl<'a> Types<'a> { Api::Include(_) => {} Api::Struct(strct) => { let ident = &strct.ident; - if !type_names.insert(ident) { - return Err(duplicate_name(strct, ident)); + if type_names.insert(ident) { + structs.insert(ident.clone(), strct); + } else { + duplicate_name(cx, strct, ident); } - structs.insert(ident.clone(), strct); for field in &strct.fields { visit(&mut all, &field.ty); } } Api::Enum(enm) => { let ident = &enm.ident; - if !type_names.insert(ident) { - return Err(duplicate_name(enm, ident)); + if type_names.insert(ident) { + enums.insert(ident.clone(), enm); + } else { + duplicate_name(cx, enm, ident); } - enums.insert(ident.clone(), enm); } Api::CxxType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - return Err(duplicate_name(ety, ident)); + duplicate_name(cx, ety, ident); } cxx.insert(ident); } Api::RustType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - return Err(duplicate_name(ety, ident)); + duplicate_name(cx, ety, ident); } rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { let ident = &efn.ident; if !function_names.insert((&efn.receiver, ident)) { - return Err(duplicate_name(efn, ident)); + duplicate_name(cx, efn, ident); } for arg in &efn.args { visit(&mut all, &arg.ty); @@ -94,13 +96,13 @@ impl<'a> Types<'a> { } } - Ok(Types { + Types { all, structs, enums, cxx, rust, - }) + } } pub fn needs_indirect_abi(&self, ty: &Type) -> bool { @@ -135,7 +137,7 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } } -fn duplicate_name(sp: impl ToTokens, ident: &Ident) -> Error { +fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { let msg = format!("the name `{}` is defined multiple times", ident); - Error::new_spanned(sp, msg) + cx.error(sp, msg); } From 4c14162d34d5c708eaff03ea403bbf3f04ad23e4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:30:09 +0000 Subject: [PATCH 534/2232] Update multi parse errors ui test --- diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr index 61c4fba..854aa90 100644 --- a/tests/ui/multiple_parse_error.stderr +++ b/tests/ui/multiple_parse_error.stderr @@ -3,3 +3,9 @@ error: struct with generic parameters is not supported yet | 3 | struct Monad; | ^^^^^^^^^^^^^^^ + +error: unrecognized ABI + --> $DIR/multiple_parse_error.rs:5:5 + | +5 | extern "Haskell" { + | ^^^^^^^^^^^^^^^^ From 29bc17710310e97152e1a6ee8562aa6c667adb94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 08:40:45 +0000 Subject: [PATCH 535/2232] Merge pull request #186 from dtolnay/batch Batch parse errors --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index c7b3c3c..6663e4b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -8,6 +8,7 @@ mod write; use self::error::{format_err, Error, Result}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use quote::quote; use std::fs; @@ -42,12 +43,15 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { }; match (|| -> Result<_> { proc_macro2::fallback::force(); + let ref mut errors = Errors::new(); let syntax = syn::parse_file(&source)?; let bridge = find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(bridge.module)?; - let ref types = Types::collect(apis)?; - check::typecheck(namespace, apis, types)?; + let ref apis = syntax::parse_items(errors, bridge.module); + let ref types = Types::collect(errors, apis); + errors.propagate()?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; let out = write::gen(namespace, apis, types, opt, header); Ok(out) })() { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 737c389..e67902b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,5 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, @@ -8,16 +9,22 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{parse_quote, Error, ItemMod, Result, Token}; -pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { - let ident = &ffi.ident; - let content = ffi.content.ok_or(Error::new( +pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { + let ref mut errors = Errors::new(); + let content = ffi.content.take().ok_or(Error::new( Span::call_site(), "#[cxx::bridge] module must have inline contents", ))?; - let ref apis = syntax::parse_items(content.1)?; - let ref types = Types::collect(apis)?; - check::typecheck(namespace, apis, types)?; + let ref apis = syntax::parse_items(errors, content.1); + let ref types = Types::collect(errors, apis); + errors.propagate()?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; + + Ok(expand(namespace, ffi, apis, types)) +} +fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); @@ -93,15 +100,16 @@ pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result { .into_iter() .filter(|attr| attr.path.is_ident("doc")); let vis = &ffi.vis; + let ident = &ffi.ident; - Ok(quote! { + quote! { #(#attrs)* #[deny(improper_ctypes)] #[allow(non_snake_case)] #vis mod #ident { #expanded } - }) + } } fn expand_struct(strct: &Struct) -> TokenStream { diff --git a/syntax/check.rs b/syntax/check.rs index 5d442ce..72b4a6c 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,6 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; use crate::syntax::{ error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, @@ -9,25 +10,21 @@ use quote::{quote, ToTokens}; use std::collections::HashSet; use std::fmt::Display; use std::u32; -use syn::{Error, Result}; pub(crate) struct Check<'a> { namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, - errors: &'a mut Vec, + errors: &'a mut Errors, } -pub(crate) fn typecheck(namespace: &Namespace, apis: &[Api], types: &Types) -> Result<()> { - let mut errors = Vec::new(); - let mut cx = Check { +pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { + do_typecheck(&mut Check { namespace, apis, types, - errors: &mut errors, - }; - do_typecheck(&mut cx); - combine_errors(errors) + errors: cx, + }); } fn do_typecheck(cx: &mut Check) { @@ -59,7 +56,7 @@ fn do_typecheck(cx: &mut Check) { impl Check<'_> { pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) { - self.errors.push(Error::new_spanned(sp, msg)); + self.errors.error(sp, msg); } } @@ -373,18 +370,6 @@ fn span_for_receiver_error(receiver: &Receiver) -> TokenStream { } } -fn combine_errors(errors: Vec) -> Result<()> { - let mut iter = errors.into_iter(); - let mut all_errors = match iter.next() { - Some(err) => err, - None => return Ok(()), - }; - for err in iter { - all_errors.combine(err); - } - Err(all_errors) -} - fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { diff --git a/syntax/mod.rs b/syntax/mod.rs index 9b1a7e5..e6c5bb8 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -10,6 +10,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; +pub mod report; pub mod set; pub mod symbol; mod tokens; diff --git a/syntax/parse.rs b/syntax/parse.rs index a0dddbd..8bf0a4a 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,3 +1,4 @@ +use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, @@ -16,27 +17,24 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(items: Vec) -> Result> { +pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => { - let strct = parse_struct(item)?; - apis.push(strct); - } - Item::Enum(item) => { - let enm = parse_enum(item)?; - apis.push(enm); - } - Item::ForeignMod(foreign_mod) => { - let functions = parse_foreign_mod(foreign_mod)?; - apis.extend(functions); - } - Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)), - _ => return Err(Error::new_spanned(item, "unsupported item")), + Item::Struct(item) => match parse_struct(item) { + Ok(strct) => apis.push(strct), + Err(err) => cx.push(err), + }, + Item::Enum(item) => match parse_enum(item) { + Ok(enm) => apis.push(enm), + Err(err) => cx.push(err), + }, + Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), + Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), + _ => cx.error(item, "unsupported item"), } } - Ok(apis) + apis } fn parse_struct(item: ItemStruct) -> Result { @@ -151,8 +149,11 @@ fn parse_variant(variant: RustVariant) -> Result { } } -fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { - let lang = parse_lang(foreign_mod.abi)?; +fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { + let lang = match parse_lang(foreign_mod.abi) { + Ok(lang) => lang, + Err(err) => return cx.push(err), + }; let api_type = match lang { Lang::Cxx => Api::CxxType, Lang::Rust => Api::RustType, @@ -165,19 +166,21 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => { - let ety = parse_extern_type(foreign)?; - items.push(api_type(ety)); - } - ForeignItem::Fn(foreign) => { - let efn = parse_extern_fn(foreign, lang)?; - items.push(api_function(efn)); - } + ForeignItem::Type(foreign) => match parse_extern_type(foreign) { + Ok(ety) => items.push(api_type(ety)), + Err(err) => cx.push(err), + }, + ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) { + Ok(efn) => items.push(api_function(efn)), + Err(err) => cx.push(err), + }, ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { - let include = foreign.mac.parse_body()?; - items.push(Api::Include(include)); + match foreign.mac.parse_body() { + Ok(include) => items.push(Api::Include(include)), + Err(err) => cx.push(err), + } } - _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")), + _ => cx.error(foreign, "unsupported foreign item"), } } @@ -198,7 +201,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result> { } } - Ok(items) + out.extend(items); } fn parse_lang(abi: Abi) -> Result { diff --git a/syntax/report.rs b/syntax/report.rs new file mode 100644 index 0000000..d1d8bc9 --- /dev/null +++ b/syntax/report.rs @@ -0,0 +1,33 @@ +use quote::ToTokens; +use std::fmt::Display; +use syn::{Error, Result}; + +pub struct Errors { + errors: Vec, +} + +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) + } +} diff --git a/syntax/types.rs b/syntax/types.rs index 4842507..02249e2 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,10 +1,10 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; use crate::syntax::{Api, Derive, Enum, Struct, Type}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; -use syn::{Error, Result}; pub struct Types<'a> { pub all: Set<'a, Type>, @@ -15,7 +15,7 @@ pub struct Types<'a> { } impl<'a> Types<'a> { - pub fn collect(apis: &'a [Api]) -> Result { + pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self { let mut all = Set::new(); let mut structs = Map::new(); let mut enums = Map::new(); @@ -50,39 +50,41 @@ impl<'a> Types<'a> { Api::Include(_) => {} Api::Struct(strct) => { let ident = &strct.ident; - if !type_names.insert(ident) { - return Err(duplicate_name(strct, ident)); + if type_names.insert(ident) { + structs.insert(ident.clone(), strct); + } else { + duplicate_name(cx, strct, ident); } - structs.insert(ident.clone(), strct); for field in &strct.fields { visit(&mut all, &field.ty); } } Api::Enum(enm) => { let ident = &enm.ident; - if !type_names.insert(ident) { - return Err(duplicate_name(enm, ident)); + if type_names.insert(ident) { + enums.insert(ident.clone(), enm); + } else { + duplicate_name(cx, enm, ident); } - enums.insert(ident.clone(), enm); } Api::CxxType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - return Err(duplicate_name(ety, ident)); + duplicate_name(cx, ety, ident); } cxx.insert(ident); } Api::RustType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - return Err(duplicate_name(ety, ident)); + duplicate_name(cx, ety, ident); } rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { let ident = &efn.ident; if !function_names.insert((&efn.receiver, ident)) { - return Err(duplicate_name(efn, ident)); + duplicate_name(cx, efn, ident); } for arg in &efn.args { visit(&mut all, &arg.ty); @@ -94,13 +96,13 @@ impl<'a> Types<'a> { } } - Ok(Types { + Types { all, structs, enums, cxx, rust, - }) + } } pub fn needs_indirect_abi(&self, ty: &Type) -> bool { @@ -135,7 +137,7 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } } -fn duplicate_name(sp: impl ToTokens, ident: &Ident) -> Error { +fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { let msg = format!("the name `{}` is defined multiple times", ident); - Error::new_spanned(sp, msg) + cx.error(sp, msg); } diff --git a/tests/ui/multiple_parse_error.rs b/tests/ui/multiple_parse_error.rs new file mode 100644 index 0000000..061eab6 --- /dev/null +++ b/tests/ui/multiple_parse_error.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + struct Monad; + + extern "Haskell" { + } +} + +fn main() {} diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr new file mode 100644 index 0000000..854aa90 --- /dev/null +++ b/tests/ui/multiple_parse_error.stderr @@ -0,0 +1,11 @@ +error: struct with generic parameters is not supported yet + --> $DIR/multiple_parse_error.rs:3:5 + | +3 | struct Monad; + | ^^^^^^^^^^^^^^^ + +error: unrecognized ABI + --> $DIR/multiple_parse_error.rs:5:5 + | +5 | extern "Haskell" { + | ^^^^^^^^^^^^^^^^ From 24d22b471abf8949dfed8810a4ba314a5b7041d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 04 2020 09:31:53 +0000 Subject: [PATCH 536/2232] Remove Travis configuration --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06cedb4..1040454 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: test +name: CI on: push: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f65f7f8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,64 +0,0 @@ -language: rust - -rust: - - nightly - - beta - - stable - -script: - - cargo run --manifest-path demo-rs/Cargo.toml - - cargo test - -matrix: - include: - - name: macOS - os: macos - rust: nightly - - - name: Windows (gnu) - os: windows - rust: nightly-x86_64-pc-windows-gnu - before_script: - # windows is bad at symlinks - - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax - - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - - - name: Windows (msvc) - os: windows - rust: nightly-x86_64-pc-windows-msvc - before_script: - - rm gen/build/src/gen gen/build/src/syntax gen/cmd/src/gen gen/cmd/src/syntax gen/src/include macro/src/syntax - - cp -r include gen/src; cp -r gen/src gen/build/src/gen; cp -r gen/src gen/cmd/src/gen; cp -r syntax gen/build/src; cp -r syntax gen/cmd/src; cp -r syntax macro/src - - - name: Buck - rust: nightly - before_install: - - sudo apt-get install -y openjdk-8-jdk - - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 - - wget -O buck.deb https://github.com/facebook/buck/releases/download/v2019.10.17.01/buck.2019.10.17.01_all.deb - - sudo dpkg -i buck.deb - before_script: - - cp third-party/Cargo.lock . - - cargo vendor --versioned-dirs --locked third-party/vendor - script: - - buck build :cxx#check --verbose=0 - - buck run demo-rs --verbose=0 - - buck test ... --verbose=0 - - - name: Bazel - rust: nightly - before_install: - - wget -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh - - chmod +x install.sh - - ./install.sh --user - before_script: - - cp third-party/Cargo.lock . - - cargo vendor --versioned-dirs --locked third-party/vendor - script: - - bazel run demo-rs --verbose_failures --noshow_progress - - bazel test ... --verbose_failures --noshow_progress - - - name: Minimum rustc - rust: 1.42.0 - script: - - cargo run --manifest-path demo-rs/Cargo.toml From 905eb2e1f5307f64850503ca1f46101239dabf17 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 04 2020 22:02:17 +0000 Subject: [PATCH 537/2232] Support enums defined in C++ code. This allows listing an enum in an extern "C" block as well as in the shared block. In this case, we do not emit the C++ declaration of the enum but instead emit static assertions that it has the values that we expect. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8ce3813..2ea6ca8 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -5,7 +5,7 @@ use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; pub(super) fn gen( namespace: &Namespace, @@ -46,6 +46,7 @@ pub(super) fn gen( } } + let mut cxx_types = HashSet::new(); let mut methods_for_type = HashMap::new(); for api in apis { if let Api::RustFunction(efn) = api { @@ -56,6 +57,9 @@ pub(super) fn gen( .push(efn); } } + if let Api::CxxType(enm) = api { + cxx_types.insert(&enm.ident); + } } for api in apis { @@ -66,7 +70,11 @@ pub(super) fn gen( } Api::Enum(enm) => { out.next_section(); - write_enum(out, enm); + if cxx_types.contains(&enm.ident) { + check_enum(out, enm); + } else { + write_enum(out, enm); + } } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { @@ -373,6 +381,34 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, "}};"); } +fn check_enum(out: &mut OutFile, enm: &Enum) { + let discriminants = enm + .variants + .iter() + .scan(None, |prev_discriminant, variant| { + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + *prev_discriminant = Some(discriminant); + Some(discriminant) + }); + writeln!( + out, + "static_assert(sizeof({}) == sizeof(uint32_t));", + enm.ident + ); + enm.variants + .iter() + .zip(discriminants) + .for_each(|(variant, discriminant)| { + writeln!( + out, + "static_assert({}::{} == {});", + enm.ident, variant.ident, discriminant + ); + }); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e67902b..1a23960 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -7,6 +7,7 @@ use crate::syntax::{ }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; +use std::collections::HashSet; use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { @@ -39,12 +40,22 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T } } + let mut enums = HashSet::new(); + for api in apis { + if let Api::Enum(enm) = api { + enums.insert(&enm.ident); + } + } for api in apis { match api { Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), - Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)), + Api::CxxType(ety) => { + if !enums.contains(&ety.ident) { + expanded.extend(expand_cxx_type(ety)); + } + } Api::CxxFunction(efn) => { expanded.extend(expand_cxx_function_shim(namespace, efn, types)); } diff --git a/syntax/types.rs b/syntax/types.rs index 02249e2..bfe7bf0 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -69,7 +69,9 @@ impl<'a> Types<'a> { } Api::CxxType(ety) => { let ident = &ety.ident; - if !type_names.insert(ident) { + // We allow declaring the same type as a shared enum and as a Cxxtype, as this + // means not to emit the C++ enum definition. + if !type_names.insert(ident) && !enums.contains_key(ident) { duplicate_name(cx, ety, ident); } cxx.insert(ident); From 0f654ffeb0037fba4aadaa098e163434406a3fd5 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 05 2020 03:04:21 +0000 Subject: [PATCH 538/2232] Fix previous commit. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 2ea6ca8..70620ce 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -5,7 +5,7 @@ use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; pub(super) fn gen( namespace: &Namespace, @@ -46,7 +46,6 @@ pub(super) fn gen( } } - let mut cxx_types = HashSet::new(); let mut methods_for_type = HashMap::new(); for api in apis { if let Api::RustFunction(efn) = api { @@ -57,9 +56,6 @@ pub(super) fn gen( .push(efn); } } - if let Api::CxxType(enm) = api { - cxx_types.insert(&enm.ident); - } } for api in apis { @@ -70,7 +66,7 @@ pub(super) fn gen( } Api::Enum(enm) => { out.next_section(); - if cxx_types.contains(&enm.ident) { + if types.cxx.contains(&enm.ident) { check_enum(out, enm); } else { write_enum(out, enm); @@ -382,31 +378,24 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - let discriminants = enm - .variants - .iter() - .scan(None, |prev_discriminant, variant| { - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - *prev_discriminant = Some(discriminant); - Some(discriminant) - }); writeln!( out, "static_assert(sizeof({}) == sizeof(uint32_t));", enm.ident ); - enm.variants - .iter() - .zip(discriminants) - .for_each(|(variant, discriminant)| { - writeln!( - out, - "static_assert({}::{} == {});", - enm.ident, variant.ident, discriminant - ); - }); + let mut prev_discriminant = None; + for variant in &enm.variants { + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + writeln!( + out, + "static_assert(static_cast({}::{}) == {}, + \"disagrees with the value in #[cxx::bridge]\");", + enm.ident, variant.ident, discriminant, + ); + prev_discriminant = Some(discriminant); + } } fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1a23960..d8810dc 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -7,7 +7,6 @@ use crate::syntax::{ }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use std::collections::HashSet; use syn::{parse_quote, Error, ItemMod, Result, Token}; pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { @@ -40,19 +39,13 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T } } - let mut enums = HashSet::new(); - for api in apis { - if let Api::Enum(enm) = api { - enums.insert(&enm.ident); - } - } for api in apis { match api { Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { - if !enums.contains(&ety.ident) { + if !types.enums.contains_key(&ety.ident) { expanded.extend(expand_cxx_type(ety)); } } diff --git a/syntax/types.rs b/syntax/types.rs index bfe7bf0..5800513 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -61,11 +61,12 @@ impl<'a> Types<'a> { } Api::Enum(enm) => { let ident = &enm.ident; - if type_names.insert(ident) { - enums.insert(ident.clone(), enm); - } else { + // We allow declaring the same type as a shared enum and as a Cxxtype, as this + // means not to emit the C++ enum definition. + if !type_names.insert(ident) && !cxx.contains(ident) { duplicate_name(cx, enm, ident); } + enums.insert(ident.clone(), enm); } Api::CxxType(ety) => { let ident = &ety.ident; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 0162c9b..16846fb 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -84,6 +84,15 @@ pub mod ffi { fn set2(&mut self, n: usize) -> usize; } + extern "C" { + type COwnedEnum; + } + + enum COwnedEnum { + CVal1, + CVal2, + } + extern "Rust" { type R; type R2; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index d3b7d38..795b4a9 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,6 +23,11 @@ private: std::vector v; }; +enum COwnedEnum { + CVal1, + CVal2, +}; + size_t c_return_primitive(); Shared c_return_shared(); rust::Box c_return_box(); From 9fe5ea3ad742ab7d465adb16e4cc52febc939c61 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 05 2020 03:25:15 +0000 Subject: [PATCH 539/2232] Add missing message. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 70620ce..3c059a7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -380,7 +380,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { fn check_enum(out: &mut OutFile, enm: &Enum) { writeln!( out, - "static_assert(sizeof({}) == sizeof(uint32_t));", + "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", enm.ident ); let mut prev_discriminant = None; From de471c9a2202e40be77ff2b5dda28d5c4882c1fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 03:49:52 +0000 Subject: [PATCH 540/2232] Merge pull request #187 from jgalenson/enums Support enums defined in C++ code. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8ce3813..3c059a7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -66,7 +66,11 @@ pub(super) fn gen( } Api::Enum(enm) => { out.next_section(); - write_enum(out, enm); + if types.cxx.contains(&enm.ident) { + check_enum(out, enm); + } else { + write_enum(out, enm); + } } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { @@ -373,6 +377,27 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, "}};"); } +fn check_enum(out: &mut OutFile, enm: &Enum) { + writeln!( + out, + "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", + enm.ident + ); + let mut prev_discriminant = None; + for variant in &enm.variants { + let discriminant = variant + .discriminant + .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + writeln!( + out, + "static_assert(static_cast({}::{}) == {}, + \"disagrees with the value in #[cxx::bridge]\");", + enm.ident, variant.ident, discriminant, + ); + prev_discriminant = Some(discriminant); + } +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e67902b..d8810dc 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -44,7 +44,11 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::Include(_) | Api::RustType(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), - Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)), + Api::CxxType(ety) => { + if !types.enums.contains_key(&ety.ident) { + expanded.extend(expand_cxx_type(ety)); + } + } Api::CxxFunction(efn) => { expanded.extend(expand_cxx_function_shim(namespace, efn, types)); } diff --git a/syntax/types.rs b/syntax/types.rs index 02249e2..5800513 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -61,15 +61,18 @@ impl<'a> Types<'a> { } Api::Enum(enm) => { let ident = &enm.ident; - if type_names.insert(ident) { - enums.insert(ident.clone(), enm); - } else { + // We allow declaring the same type as a shared enum and as a Cxxtype, as this + // means not to emit the C++ enum definition. + if !type_names.insert(ident) && !cxx.contains(ident) { duplicate_name(cx, enm, ident); } + enums.insert(ident.clone(), enm); } Api::CxxType(ety) => { let ident = &ety.ident; - if !type_names.insert(ident) { + // We allow declaring the same type as a shared enum and as a Cxxtype, as this + // means not to emit the C++ enum definition. + if !type_names.insert(ident) && !enums.contains_key(ident) { duplicate_name(cx, ety, ident); } cxx.insert(ident); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 0162c9b..16846fb 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -84,6 +84,15 @@ pub mod ffi { fn set2(&mut self, n: usize) -> usize; } + extern "C" { + type COwnedEnum; + } + + enum COwnedEnum { + CVal1, + CVal2, + } + extern "Rust" { type R; type R2; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index d3b7d38..795b4a9 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -23,6 +23,11 @@ private: std::vector v; }; +enum COwnedEnum { + CVal1, + CVal2, +}; + size_t c_return_primitive(); Shared c_return_shared(); rust::Box c_return_box(); From 8854773c56ebf4f318d7dba3d022626259178ec0 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 05 2020 15:32:32 +0000 Subject: [PATCH 541/2232] Compute enum discriminant during parsing This allows us to reuse the computation in multiple places later. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3c059a7..4e98674 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -367,12 +367,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } writeln!(out, "enum class {} : uint32_t {{", enm.ident); for variant in &enm.variants { - write!(out, " "); - write!(out, "{}", variant.ident); - if let Some(discriminant) = &variant.discriminant { - write!(out, " = {}", discriminant); - } - writeln!(out, ","); + writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); } @@ -383,18 +378,13 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", enm.ident ); - let mut prev_discriminant = None; for variant in &enm.variants { - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); writeln!( out, "static_assert(static_cast({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, discriminant, + enm.ident, variant.ident, variant.discriminant, ); - prev_discriminant = Some(discriminant); } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d8810dc..fcb845c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -139,19 +139,13 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; - let variants = enm - .variants - .iter() - .scan(None, |prev_discriminant, variant| { - let variant_ident = &variant.ident; - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - *prev_discriminant = Some(discriminant); - Some(quote! { - pub const #variant_ident: Self = #ident { repr: #discriminant }; - }) - }); + let variants = enm.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + let discriminant = &variant.discriminant; + Some(quote! { + pub const #variant_ident: Self = #ident { repr: #discriminant }; + }) + }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/syntax/check.rs b/syntax/check.rs index 72b4a6c..c54430c 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -7,9 +7,7 @@ use crate::syntax::{ }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; -use std::collections::HashSet; use std::fmt::Display; -use std::u32; pub(crate) struct Check<'a> { namespace: &'a Namespace, @@ -191,25 +189,6 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { let span = span_for_enum_error(enm); cx.error(span, "enums without any variants are not supported"); } - - let mut discriminants = HashSet::new(); - enm.variants - .iter() - .fold(None, |prev_discriminant, variant| { - if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { - let msg = format!("overflowed on value after {}", prev_discriminant.unwrap()); - cx.error(span_for_enum_error(enm), msg); - return None; - } - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - cx.error(span_for_enum_error(enm), msg); - } - Some(discriminant) - }); } fn check_api_type(cx: &mut Check, ty: &ExternType) { diff --git a/syntax/mod.rs b/syntax/mod.rs index e6c5bb8..fd8db73 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -95,7 +95,7 @@ pub struct Receiver { pub struct Variant { pub ident: Ident, - pub discriminant: Option, + pub discriminant: u32, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index 8bf0a4a..fc51ee9 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -5,6 +5,7 @@ use crate::syntax::{ Struct, Ty1, Type, Var, Variant, }; use quote::{format_ident, quote}; +use std::collections::HashSet; use syn::punctuated::Punctuated; use syn::{ Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, @@ -96,16 +97,33 @@ fn parse_enum(item: ItemEnum) -> Result { let doc = attrs::parse_doc(&item.attrs)?; - for variant in &item.variants { - match &variant.fields { + let mut variants = Vec::new(); + let mut discriminants = HashSet::new(); + let mut prev_discriminant = None; + for variant in item.variants { + match variant.fields { Fields::Unit => {} _ => { return Err(Error::new_spanned( variant, "enums with data are not supported yet", - )) + )); } } + if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { + return Err(Error::new_spanned(variant, "overflowed on value")); + } + let discriminant = + parse_discriminant(&variant)?.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + return Err(Error::new_spanned(variant, msg)); + } + variants.push(Variant { + ident: variant.ident, + discriminant: discriminant, + }); + prev_discriminant = Some(discriminant); } Ok(Api::Enum(Enum { @@ -113,30 +131,20 @@ fn parse_enum(item: ItemEnum) -> Result { enum_token: item.enum_token, ident: item.ident, brace_token: item.brace_token, - variants: item - .variants - .into_iter() - .map(parse_variant) - .collect::>()?, + variants: variants, })) } -fn parse_variant(variant: RustVariant) -> Result { +fn parse_discriminant(variant: &RustVariant) -> Result> { match &variant.discriminant { - None => Ok(Variant { - ident: variant.ident, - discriminant: None, - }), + None => Ok(None), Some(( _, Expr::Lit(ExprLit { lit: Lit::Int(n), .. }), )) => match n.base10_parse() { - Ok(val) => Ok(Variant { - ident: variant.ident, - discriminant: Some(val), - }), + Ok(val) => Ok(Some(val)), Err(_) => Err(Error::new_spanned( variant, "cannot parse enum discriminant as an integer", diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr index f5a879f..14505e3 100644 --- a/tests/ui/duplicate_enum_discriminants.stderr +++ b/tests/ui/duplicate_enum_discriminants.stderr @@ -1,18 +1,11 @@ error: discriminant value `10` already exists - --> $DIR/duplicate_enum_discriminants.rs:3:5 + --> $DIR/duplicate_enum_discriminants.rs:5:9 | -3 | / enum A { -4 | | V1 = 10, -5 | | V2 = 10, -6 | | } - | |_____^ +5 | V2 = 10, + | ^^^^^^^ error: discriminant value `11` already exists - --> $DIR/duplicate_enum_discriminants.rs:8:5 + --> $DIR/duplicate_enum_discriminants.rs:11:9 | -8 | / enum B { -9 | | V1 = 10, -10 | | V2, -11 | | V3 = 11, -12 | | } - | |_____^ +11 | V3 = 11, + | ^^^^^^^ diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr index 3d1b370..1a22146 100644 --- a/tests/ui/enum_overflows.stderr +++ b/tests/ui/enum_overflows.stderr @@ -1,9 +1,5 @@ -error: overflowed on value after 4294967295 - --> $DIR/enum_overflows.rs:10:5 +error: overflowed on value + --> $DIR/enum_overflows.rs:13:9 | -10 | / enum Bad { -11 | | D = 0xfffffffe, -12 | | E, -13 | | F, -14 | | } - | |_____^ +13 | F, + | ^ From 8ef1bc809c6c0cb9df7750248f04dbbdd9f96765 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: May 05 2020 15:45:49 +0000 Subject: [PATCH 542/2232] Fix rustc 1.42.0. --- diff --git a/syntax/parse.rs b/syntax/parse.rs index fc51ee9..880e2c7 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,6 +6,7 @@ use crate::syntax::{ }; use quote::{format_ident, quote}; use std::collections::HashSet; +use std::u32; use syn::punctuated::Punctuated; use syn::{ Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, From acc64cb20179b7c1b72394931e2c2db51c614293 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:09:16 +0000 Subject: [PATCH 543/2232] Merge pull request #189 from jgalenson/enums Compute enum discriminant during parsing --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3c059a7..4e98674 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -367,12 +367,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } writeln!(out, "enum class {} : uint32_t {{", enm.ident); for variant in &enm.variants { - write!(out, " "); - write!(out, "{}", variant.ident); - if let Some(discriminant) = &variant.discriminant { - write!(out, " = {}", discriminant); - } - writeln!(out, ","); + writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); } @@ -383,18 +378,13 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", enm.ident ); - let mut prev_discriminant = None; for variant in &enm.variants { - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); writeln!( out, "static_assert(static_cast({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, discriminant, + enm.ident, variant.ident, variant.discriminant, ); - prev_discriminant = Some(discriminant); } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d8810dc..fcb845c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -139,19 +139,13 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; - let variants = enm - .variants - .iter() - .scan(None, |prev_discriminant, variant| { - let variant_ident = &variant.ident; - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - *prev_discriminant = Some(discriminant); - Some(quote! { - pub const #variant_ident: Self = #ident { repr: #discriminant }; - }) - }); + let variants = enm.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + let discriminant = &variant.discriminant; + Some(quote! { + pub const #variant_ident: Self = #ident { repr: #discriminant }; + }) + }); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/syntax/check.rs b/syntax/check.rs index 72b4a6c..c54430c 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -7,9 +7,7 @@ use crate::syntax::{ }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; -use std::collections::HashSet; use std::fmt::Display; -use std::u32; pub(crate) struct Check<'a> { namespace: &'a Namespace, @@ -191,25 +189,6 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { let span = span_for_enum_error(enm); cx.error(span, "enums without any variants are not supported"); } - - let mut discriminants = HashSet::new(); - enm.variants - .iter() - .fold(None, |prev_discriminant, variant| { - if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { - let msg = format!("overflowed on value after {}", prev_discriminant.unwrap()); - cx.error(span_for_enum_error(enm), msg); - return None; - } - let discriminant = variant - .discriminant - .unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - cx.error(span_for_enum_error(enm), msg); - } - Some(discriminant) - }); } fn check_api_type(cx: &mut Check, ty: &ExternType) { diff --git a/syntax/mod.rs b/syntax/mod.rs index e6c5bb8..fd8db73 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -95,7 +95,7 @@ pub struct Receiver { pub struct Variant { pub ident: Ident, - pub discriminant: Option, + pub discriminant: u32, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index 8bf0a4a..880e2c7 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -5,6 +5,8 @@ use crate::syntax::{ Struct, Ty1, Type, Var, Variant, }; use quote::{format_ident, quote}; +use std::collections::HashSet; +use std::u32; use syn::punctuated::Punctuated; use syn::{ Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, @@ -96,16 +98,33 @@ fn parse_enum(item: ItemEnum) -> Result { let doc = attrs::parse_doc(&item.attrs)?; - for variant in &item.variants { - match &variant.fields { + let mut variants = Vec::new(); + let mut discriminants = HashSet::new(); + let mut prev_discriminant = None; + for variant in item.variants { + match variant.fields { Fields::Unit => {} _ => { return Err(Error::new_spanned( variant, "enums with data are not supported yet", - )) + )); } } + if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { + return Err(Error::new_spanned(variant, "overflowed on value")); + } + let discriminant = + parse_discriminant(&variant)?.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); + if !discriminants.insert(discriminant) { + let msg = format!("discriminant value `{}` already exists", discriminant); + return Err(Error::new_spanned(variant, msg)); + } + variants.push(Variant { + ident: variant.ident, + discriminant: discriminant, + }); + prev_discriminant = Some(discriminant); } Ok(Api::Enum(Enum { @@ -113,30 +132,20 @@ fn parse_enum(item: ItemEnum) -> Result { enum_token: item.enum_token, ident: item.ident, brace_token: item.brace_token, - variants: item - .variants - .into_iter() - .map(parse_variant) - .collect::>()?, + variants: variants, })) } -fn parse_variant(variant: RustVariant) -> Result { +fn parse_discriminant(variant: &RustVariant) -> Result> { match &variant.discriminant { - None => Ok(Variant { - ident: variant.ident, - discriminant: None, - }), + None => Ok(None), Some(( _, Expr::Lit(ExprLit { lit: Lit::Int(n), .. }), )) => match n.base10_parse() { - Ok(val) => Ok(Variant { - ident: variant.ident, - discriminant: Some(val), - }), + Ok(val) => Ok(Some(val)), Err(_) => Err(Error::new_spanned( variant, "cannot parse enum discriminant as an integer", diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr index f5a879f..14505e3 100644 --- a/tests/ui/duplicate_enum_discriminants.stderr +++ b/tests/ui/duplicate_enum_discriminants.stderr @@ -1,18 +1,11 @@ error: discriminant value `10` already exists - --> $DIR/duplicate_enum_discriminants.rs:3:5 + --> $DIR/duplicate_enum_discriminants.rs:5:9 | -3 | / enum A { -4 | | V1 = 10, -5 | | V2 = 10, -6 | | } - | |_____^ +5 | V2 = 10, + | ^^^^^^^ error: discriminant value `11` already exists - --> $DIR/duplicate_enum_discriminants.rs:8:5 + --> $DIR/duplicate_enum_discriminants.rs:11:9 | -8 | / enum B { -9 | | V1 = 10, -10 | | V2, -11 | | V3 = 11, -12 | | } - | |_____^ +11 | V3 = 11, + | ^^^^^^^ diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr index 3d1b370..1a22146 100644 --- a/tests/ui/enum_overflows.stderr +++ b/tests/ui/enum_overflows.stderr @@ -1,9 +1,5 @@ -error: overflowed on value after 4294967295 - --> $DIR/enum_overflows.rs:10:5 +error: overflowed on value + --> $DIR/enum_overflows.rs:13:9 | -10 | / enum Bad { -11 | | D = 0xfffffffe, -12 | | E, -13 | | F, -14 | | } - | |_____^ +13 | F, + | ^ From 7ae018fb5a37cd8c8622892bc2900f729976e63e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:11:30 +0000 Subject: [PATCH 544/2232] Resolve redundant_field_names lint --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 880e2c7..1f6cbac 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -122,7 +122,7 @@ fn parse_enum(item: ItemEnum) -> Result { } variants.push(Variant { ident: variant.ident, - discriminant: discriminant, + discriminant, }); prev_discriminant = Some(discriminant); } @@ -132,7 +132,7 @@ fn parse_enum(item: ItemEnum) -> Result { enum_token: item.enum_token, ident: item.ident, brace_token: item.brace_token, - variants: variants, + variants, })) } From 39ee0ed36bb7646642967a09806ce3cbeb53f013 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:12:29 +0000 Subject: [PATCH 545/2232] Resolve assign_op_pattern lint --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 0d952f9..bfb4a4e 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -88,8 +88,9 @@ where type Item = &'a T; fn next(&mut self) -> Option { - self.index = self.index + 1; - self.v.get(self.index - 1) + let next = self.v.get(self.index); + self.index += 1; + next } } From c79ba750b0796010ef852c63a034dff19b41e6b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:13:00 +0000 Subject: [PATCH 546/2232] Suppress len_without_is_empty lint --- diff --git a/src/lib.rs b/src/lib.rs index 86318c8..cc0dcf2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -350,6 +350,7 @@ clippy::declare_interior_mutable_const, clippy::inherent_to_string, clippy::large_enum_variant, + clippy::len_without_is_empty, clippy::missing_safety_doc, clippy::module_inception, clippy::needless_doctest_main, From db450b0a9de864e147d9c881e30d629efadd4cf6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:16:57 +0000 Subject: [PATCH 547/2232] Suppress some lints in cxx-build --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 5bee67f..cabe2b1 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -45,6 +45,13 @@ //! $ cxxbridge src/main.rs > path/to/mybridge.cc //! ``` +#![allow( + clippy::inherent_to_string, + clippy::needless_doctest_main, + clippy::new_without_default, + clippy::toplevel_ref_arg +)] + mod error; mod gen; mod paths; From 884d91f0bc86242a6a984cbe6335cf99cf57f3cd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:17:23 +0000 Subject: [PATCH 548/2232] Resolve absurd_extreme_comparisons lint --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 16846fb..e298e88 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -218,7 +218,7 @@ fn r_return_sum(n1: usize, n2: usize) -> usize { } fn r_return_enum(n: u32) -> ffi::Enum { - if n <= 0 { + if n == 0 { ffi::Enum::AVal } else if n <= 2020 { ffi::Enum::BVal From a3f6407efac747dc09c73b4edc34799ffdf79abf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:28:00 +0000 Subject: [PATCH 549/2232] Expand discriminant overflow error message Renders as: error[cxxbridge]: discriminant overflow on value after 4294967295 ┌─ src/main.rs:11:9 │ 11 │ B, │ ^ discriminant overflow │ = note: explicitly set `= 0` if that is desired outcome This more closely matches rustc's error message, which is: error[E0370]: enum discriminant overflowed --> src/lib.rs:4:5 | 4 | B, | ^ overflowed on value after 255 | = note: explicitly set `B = 0` if that is desired outcome --- diff --git a/syntax/error.rs b/syntax/error.rs index f2cb8d1..a60b7da 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -18,6 +18,7 @@ pub static ERRORS: &[Error] = &[ CXXBRIDGE_RESERVED, CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, + DISCRIMINANT_OVERFLOW, DOUBLE_UNDERSCORE, RUST_TYPE_BY_VALUE, USE_NOT_ALLOWED, @@ -47,6 +48,12 @@ pub static CXX_TYPE_BY_VALUE: Error = Error { note: Some("hint: wrap it in a UniquePtr<>"), }; +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 DOUBLE_UNDERSCORE: Error = Error { msg: "identifiers containing double underscore are reserved in C++", label: Some("reserved identifier"), diff --git a/syntax/parse.rs b/syntax/parse.rs index 1f6cbac..7e4422d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -112,7 +112,8 @@ fn parse_enum(item: ItemEnum) -> Result { } } if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { - return Err(Error::new_spanned(variant, "overflowed on value")); + let msg = format!("discriminant overflow on value after {}", u32::MAX); + return Err(Error::new_spanned(variant, msg)); } let discriminant = parse_discriminant(&variant)?.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr index 1a22146..db92f7f 100644 --- a/tests/ui/enum_overflows.stderr +++ b/tests/ui/enum_overflows.stderr @@ -1,4 +1,4 @@ -error: overflowed on value +error: discriminant overflow on value after 4294967295 --> $DIR/enum_overflows.rs:13:9 | 13 | F, From 4301f3ca51cada2595248758daf64d40be5bbfb1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:29:52 +0000 Subject: [PATCH 550/2232] Simplify discriminant overflow check --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 7e4422d..602a269 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -111,7 +111,7 @@ fn parse_enum(item: ItemEnum) -> Result { )); } } - if variant.discriminant.is_none() && prev_discriminant.unwrap_or(0) == u32::MAX { + if variant.discriminant.is_none() && prev_discriminant == Some(u32::MAX) { let msg = format!("discriminant overflow on value after {}", u32::MAX); return Err(Error::new_spanned(variant, msg)); } From dfbce4bbc566d356de7c27ad86117b2c14d1605c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 17:52:12 +0000 Subject: [PATCH 551/2232] Remove unreachable return case from cxx function shim This is unreachable because returning a CxxVector by value is not allowed. error[cxxbridge]: returning C++ vector by value is not supported ┌─ lib.rs:30:29 │ 30 │ fn not_allowed() -> CxxVector; │ ^^^^^^^^^^^^^ returning C++ vector by value is not supported │ --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 4e98674..f28a58e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -522,10 +522,6 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::CxxVector(_)) => write!( - out, - " /* Use RVO to convert to r-value and move construct */" - ), Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } From 9531334f5a73c50d6ced9c6ff53681a3c89a5253 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 19:54:15 +0000 Subject: [PATCH 552/2232] Move github funding.yml out of repo root --- diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7507077 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/FUNDING.yml b/FUNDING.yml deleted file mode 100644 index 7507077..0000000 --- a/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: dtolnay From 29061ea1d89704500f3a18f2038df9c5721f534f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 05 2020 20:56:54 +0000 Subject: [PATCH 553/2232] Update build status badge to GitHub Actions --- diff --git a/README.md b/README.md index f1cf00f..1b1611d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ CXX — safe FFI between Rust and C++ ========================================= -[![Build Status](https://api.travis-ci.com/dtolnay/cxx.svg?branch=master)](https://travis-ci.com/dtolnay/cxx) +[![Build Status](https://img.shields.io/github/workflow/status/dtolnay/cxx/CI/master)](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster) [![Latest Version](https://img.shields.io/crates/v/cxx.svg)](https://crates.io/crates/cxx) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/cxx) From 295ef6b38a1cf7b8f0b0029c5fa618bf515bb101 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 07 2020 23:10:30 +0000 Subject: [PATCH 554/2232] Adjust foreign item parsing to prepare for type aliases This will be required for parse_extern_type to return a different kind of Api for the case of type aliases. --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 602a269..b794ba9 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -164,24 +164,16 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Ok(lang) => lang, Err(err) => return cx.push(err), }; - let api_type = match lang { - Lang::Cxx => Api::CxxType, - Lang::Rust => Api::RustType, - }; - let api_function = match lang { - Lang::Cxx => Api::CxxFunction, - Lang::Rust => Api::RustFunction, - }; let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(foreign) { - Ok(ety) => items.push(api_type(ety)), + ForeignItem::Type(foreign) => match parse_extern_type(foreign, lang) { + Ok(ety) => items.push(ety), Err(err) => cx.push(err), }, ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) { - Ok(efn) => items.push(api_function(efn)), + Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { @@ -231,18 +223,22 @@ fn parse_lang(abi: Abi) -> Result { } } -fn parse_extern_type(foreign_type: &ForeignItemType) -> Result { +fn parse_extern_type(foreign_type: &ForeignItemType, lang: Lang) -> Result { let doc = attrs::parse_doc(&foreign_type.attrs)?; let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); - Ok(ExternType { + let api_type = match lang { + Lang::Cxx => Api::CxxType, + Lang::Rust => Api::RustType, + }; + Ok(api_type(ExternType { doc, type_token, ident, - }) + })) } -fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -318,8 +314,12 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let ident = foreign_fn.sig.ident.clone(); let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; + let api_function = match lang { + Lang::Cxx => Api::CxxFunction, + Lang::Rust => Api::RustFunction, + }; - Ok(ExternFn { + Ok(api_function(ExternFn { lang, doc, ident, @@ -333,7 +333,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { throws_tokens, }, semi_token, - }) + })) } fn parse_type(ty: &RustType) -> Result { From 6e80833572b3bf6e7b47cb54ebbd5d79cc97d4f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:44:11 +0000 Subject: [PATCH 555/2232] Support code generation for multiple cxx::bridge files in one Build --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index cabe2b1..9e8475a 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -62,6 +62,7 @@ use crate::gen::Opt; use anyhow::anyhow; use std::fs; use std::io::{self, Write}; +use std::iter; use std::path::Path; use std::process; @@ -72,16 +73,31 @@ use std::process; /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile #[must_use] pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { - match try_generate_bridge(rust_source_file.as_ref()) { - Ok(build) => build, - Err(err) => { + bridges(iter::once(rust_source_file)) +} + +/// `cxx_build::bridge` but for when more than one file contains a +/// #\[cxx::bridge\] module. +/// +/// ```no_run +/// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; +/// cxx_build::bridges(source_files) +/// .file("../demo-cxx/demo.cc") +/// .flag("-std=c++11") +/// .compile("cxxbridge-demo"); +/// ``` +pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { + let mut build = paths::cc_build(); + for path in rust_source_files { + if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); process::exit(1); } } + build } -fn try_generate_bridge(rust_source_file: &Path) -> Result { +fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> { let header = gen::do_generate_header(rust_source_file, Opt::default()); let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; @@ -91,7 +107,6 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; fs::write(&bridge_path, bridge)?; - let mut build = paths::cc_build(); build.file(&bridge_path); let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); @@ -99,5 +114,5 @@ fn try_generate_bridge(rust_source_file: &Path) -> Result { let _ = fs::remove_file(cxx_h); let _ = fs::write(cxx_h, gen::include::HEADER); - Ok(build) + Ok(()) } From db08d4affec5fa375736e7652ebe54c253bc29e1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:01 +0000 Subject: [PATCH 556/2232] Pull in quote 1.0.4 Includes https://github.com/dtolnay/quote/pull/151 which will be used in the implementation of type_id. --- diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4c2d423..18b46e5 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -13,7 +13,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -quote = "1.0" +quote = "1.0.4" syn = { version = "1.0", features = ["full"] } [dev-dependencies] From d09e012608fbe9c0e6929e33ae5f8dc214fbb1d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:01 +0000 Subject: [PATCH 557/2232] Add a type-level encoding of qualified paths --- diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b56f58e..bc8d19b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -11,10 +11,11 @@ extern crate proc_macro; mod expand; mod syntax; +mod type_id; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod}; +use syn::{parse_macro_input, ItemMod, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,3 +45,9 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +#[proc_macro] +pub fn type_id(input: TokenStream) -> TokenStream { + let arg = parse_macro_input!(input as LitStr); + type_id::expand(arg).into() +} diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs new file mode 100644 index 0000000..15a5833 --- /dev/null +++ b/macro/src/type_id.rs @@ -0,0 +1,29 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::LitStr; + +// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` +pub fn expand(arg: LitStr) -> TokenStream { + let mut ids = Vec::new(); + + for word in arg.value().split("::") { + if !ids.is_empty() { + ids.push(quote!(())); + } + for ch in word.chars() { + ids.push(match ch { + 'A'..='Z' | 'a'..='z' => { + let t = format_ident!("{}", ch); + quote!(::cxx::private::#t) + } + '0'..='9' | '_' => { + let t = format_ident!("_{}", ch); + quote!(::cxx::private::#t) + } + _ => quote!([(); #ch as _]), + }); + } + } + + quote! { (#(#ids,)*) } +} diff --git a/src/lib.rs b/src/lib.rs index cc0dcf2..39b1ecc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -376,6 +376,7 @@ mod rust_sliceu8; mod rust_str; mod rust_string; mod rust_vec; +mod type_id; mod unique_ptr; mod unwind; @@ -386,7 +387,7 @@ pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; pub use crate::unique_ptr::UniquePtr; -pub use cxxbridge_macro::bridge; +pub use cxxbridge_macro::{bridge, type_id}; // Not public API. #[doc(hidden)] @@ -399,6 +400,7 @@ pub mod private { pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::rust_vec::RustVec; + pub use crate::type_id::*; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } diff --git a/src/type_id.rs b/src/type_id.rs new file mode 100644 index 0000000..68020ea --- /dev/null +++ b/src/type_id.rs @@ -0,0 +1,16 @@ +#![allow(non_camel_case_types)] + +macro_rules! chars { + ($($ch:ident)*) => { + $( + pub enum $ch {} + )* + }; +} + +chars! { + _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + a b c d e f g h i j k l m n o p q r s t u v w x y z + __ // underscore +} From 9938381aa777ac49e973d322e48b1aa3aacd2ac9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 558/2232] Introduce type aliases in syntax tree --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fcb845c..6eac0d9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -55,6 +55,7 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::RustFunction(efn) => { hidden.extend(expand_rust_function_shim(namespace, efn, types)) } + Api::TypeAlias(_alias) => unimplemented!(), } } diff --git a/syntax/ident.rs b/syntax/ident.rs index cec424c..41eef3b 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -42,6 +42,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &arg.ident); } } + Api::TypeAlias(_alias) => unimplemented!(), } } } diff --git a/syntax/mod.rs b/syntax/mod.rs index fd8db73..3eb6c6e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -20,7 +20,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Lifetime, LitStr, Token}; +use syn::{Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -35,6 +35,7 @@ pub enum Api { CxxFunction(ExternFn), RustType(ExternType), RustFunction(ExternFn), + TypeAlias(TypeAlias), } pub struct ExternType { @@ -68,6 +69,14 @@ pub struct ExternFn { pub semi_token: Token![;], } +pub struct TypeAlias { + pub type_token: Token![type], + pub ident: Ident, + pub eq_token: Token![=], + pub ty: RustType, + pub semi_token: Token![;], +} + pub struct Signature { pub fn_token: Token![fn], pub receiver: Option, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 934c85b..6dd6073 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, Var, + Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, + TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -86,6 +87,14 @@ impl ToTokens for ExternType { } } +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.ident.to_tokens(tokens); + } +} + impl ToTokens for Struct { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/syntax/types.rs b/syntax/types.rs index 5800513..2ab8cbd 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -96,6 +96,7 @@ impl<'a> Types<'a> { visit(&mut all, ret); } } + Api::TypeAlias(_alias) => unimplemented!(), } } From e47119ca2fa337fa585b6079e022f7d221e320b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 559/2232] Pull in syn 1.0.19 Includes https://github.com/dtolnay/syn/pull/790 which we need for extern type aliases. --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 1cbcced..16aeba3 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,7 +13,7 @@ cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 57f2e39..92adb89 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" structopt = "0.3" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 18b46e5..8ade852 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -14,7 +14,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0.4" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [dev-dependencies] cxx = { version = "0.3", path = ".." } diff --git a/third-party/BUCK b/third-party/BUCK index 2de784b..e16bad2 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 4d8fd9f..45b1163 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3397192..231863d 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" dependencies = [ "proc-macro2", "quote", From e2f9ec4c891ef06683cdb63ff4076c4c918e75e8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 560/2232] Type alias parsing --- diff --git a/syntax/parse.rs b/syntax/parse.rs index b794ba9..b41f3df 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -2,17 +2,19 @@ use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, Var, Variant, + Struct, Ty1, Type, TypeAlias, Var, Variant, }; +use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::HashSet; use std::u32; +use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, - Variant as RustVariant, + Abi, Attribute, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, + ForeignItemType, GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, + PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, + TypeReference, TypeSlice, Variant as RustVariant, }; pub mod kw { @@ -182,6 +184,10 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), } } + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(tokens, lang) { + Ok(api) => items.push(api), + Err(err) => cx.push(err), + }, _ => cx.error(foreign, "unsupported foreign item"), } } @@ -336,6 +342,44 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { })) } +fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { + // type Alias = crate::path::to::Type; + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + let type_token: Token![type] = match input.parse()? { + Some(type_token) => type_token, + None => { + let span = input.cursor().token_stream(); + return Err(Error::new_spanned(span, "unsupported foreign item")); + } + }; + let ident: Ident = input.parse()?; + let eq_token: Token![=] = input.parse()?; + let ty: RustType = input.parse()?; + let semi_token: Token![;] = input.parse()?; + attrs::parse_doc(&attrs)?; + + Ok(TypeAlias { + type_token, + ident, + eq_token, + ty, + semi_token, + }) + } + + let type_alias = parse.parse2(tokens.clone())?; + match lang { + Lang::Cxx => Ok(Api::TypeAlias(type_alias)), + Lang::Rust => { + let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token); + let span = quote!(#type_token #semi_token); + let msg = "type alias in extern \"Rust\" block is not supported"; + Err(Error::new_spanned(span, msg)) + } + } +} + fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), diff --git a/tests/ui/type_alias_rust.rs b/tests/ui/type_alias_rust.rs new file mode 100644 index 0000000..67df489 --- /dev/null +++ b/tests/ui/type_alias_rust.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + /// Incorrect. + type Alias = crate::Type; + } +} + +fn main() {} diff --git a/tests/ui/type_alias_rust.stderr b/tests/ui/type_alias_rust.stderr new file mode 100644 index 0000000..1b08f67 --- /dev/null +++ b/tests/ui/type_alias_rust.stderr @@ -0,0 +1,5 @@ +error: type alias in extern "Rust" block is not supported + --> $DIR/type_alias_rust.rs:5:9 + | +5 | type Alias = crate::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 3118fa6606b8705af46febb078dbcd96e7b41a2b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 561/2232] Type alias ident checking --- diff --git a/syntax/ident.rs b/syntax/ident.rs index 41eef3b..7545e92 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -42,7 +42,9 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &arg.ident); } } - Api::TypeAlias(_alias) => unimplemented!(), + Api::TypeAlias(alias) => { + check(cx, &alias.ident); + } } } } From 379d342779696f38ee67ae8a3d6367c7a2caf8e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 562/2232] Type alias type info --- diff --git a/syntax/types.rs b/syntax/types.rs index 2ab8cbd..987d618 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -96,7 +96,13 @@ impl<'a> Types<'a> { visit(&mut all, ret); } } - Api::TypeAlias(_alias) => unimplemented!(), + Api::TypeAlias(alias) => { + let ident = &alias.ident; + if !type_names.insert(ident) { + duplicate_name(cx, alias, ident); + } + cxx.insert(ident); + } } } From 5f9e8ca1a00b31cfd328da216f6ec6ee45165e13 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 563/2232] Type alias code generation --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6eac0d9..e3026ba 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -3,7 +3,7 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -46,7 +46,7 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { if !types.enums.contains_key(&ety.ident) { - expanded.extend(expand_cxx_type(ety)); + expanded.extend(expand_cxx_type(namespace, ety)); } } Api::CxxFunction(efn) => { @@ -55,7 +55,10 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::RustFunction(efn) => { hidden.extend(expand_rust_function_shim(namespace, efn, types)) } - Api::TypeAlias(_alias) => unimplemented!(), + Api::TypeAlias(alias) => { + expanded.extend(expand_type_alias(alias)); + hidden.extend(expand_type_alias_verify(namespace, alias)); + } } } @@ -162,15 +165,21 @@ fn expand_enum(enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(ety: &ExternType) -> TokenStream { +fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; + let type_id = type_id(namespace, ident); + quote! { #doc #[repr(C)] pub struct #ident { _private: ::cxx::private::Opaque, } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + } } } @@ -555,6 +564,35 @@ fn expand_rust_function_shim_impl( } } +fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let ty = &alias.ty; + quote! { + pub type #ident = #ty; + } +} + +fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let type_id = type_id(namespace, ident); + quote! { + const _: fn() = ::cxx::private::verify_extern_type::<#ident, #type_id>; + } +} + +fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { + let mut path = String::new(); + for name in namespace { + path += &name.to_string(); + path += "::"; + } + path += &ident.to_string(); + + quote! { + ::cxx::type_id!(#path) + } +} + fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); diff --git a/src/extern_type.rs b/src/extern_type.rs new file mode 100644 index 0000000..f7c376b --- /dev/null +++ b/src/extern_type.rs @@ -0,0 +1,6 @@ +pub unsafe trait ExternType { + type Id; +} + +#[doc(hidden)] +pub fn verify_extern_type, Id>() {} diff --git a/src/lib.rs b/src/lib.rs index 39b1ecc..d15d441 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -369,6 +369,7 @@ mod macros; mod cxx_string; mod cxx_vector; mod exception; +mod extern_type; mod function; mod opaque; mod result; @@ -386,6 +387,7 @@ mod symbols; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; +pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::{bridge, type_id}; @@ -393,6 +395,7 @@ pub use cxxbridge_macro::{bridge, type_id}; #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; + pub use crate::extern_type::verify_extern_type; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; From 9f07303af10661133a212f302ffb04804ffcfca4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 564/2232] Add ui test of type_id mismatch --- diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs new file mode 100644 index 0000000..81a9b3f --- /dev/null +++ b/tests/ui/wrong_type_id.rs @@ -0,0 +1,15 @@ +#[cxx::bridge(namespace = folly)] +mod here { + extern "C" { + type StringPiece; + } +} + +#[cxx::bridge(namespace = folly)] +mod there { + extern "C" { + type ByteRange = crate::here::StringPiece; + } +} + +fn main() {} diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr new file mode 100644 index 0000000..e6b9fc7 --- /dev/null +++ b/tests/ui/wrong_type_id.stderr @@ -0,0 +1,14 @@ +error[E0271]: type mismatch resolving `::Id == (cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::B, cxx::type_id::y, cxx::type_id::t, cxx::type_id::e, cxx::type_id::R, cxx::type_id::a, cxx::type_id::n, cxx::type_id::g, cxx::type_id::e)` + --> $DIR/wrong_type_id.rs:8:1 + | +8 | #[cxx::bridge(namespace = folly)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements + | + ::: $WORKSPACE/src/extern_type.rs:6:41 + | +6 | pub fn verify_extern_type, Id>() {} + | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | + = note: expected tuple `(cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::B, cxx::type_id::y, cxx::type_id::t, cxx::type_id::e, cxx::type_id::R, cxx::type_id::a, cxx::type_id::n, cxx::type_id::g, cxx::type_id::e)` + found tuple `(cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::S, cxx::type_id::t, cxx::type_id::r, cxx::type_id::i, cxx::type_id::n, cxx::type_id::g, cxx::type_id::P, cxx::type_id::i, cxx::type_id::e, cxx::type_id::c, cxx::type_id::e)` + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From c6ba2d27f486895999feeb4c6753120897be8872 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 02:46:02 +0000 Subject: [PATCH 565/2232] Condense type ids rendered by rustc --- diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs index 15a5833..445da2b 100644 --- a/macro/src/type_id.rs +++ b/macro/src/type_id.rs @@ -14,11 +14,11 @@ pub fn expand(arg: LitStr) -> TokenStream { ids.push(match ch { 'A'..='Z' | 'a'..='z' => { let t = format_ident!("{}", ch); - quote!(::cxx::private::#t) + quote!(::cxx::#t) } '0'..='9' | '_' => { let t = format_ident!("_{}", ch); - quote!(::cxx::private::#t) + quote!(::cxx::#t) } _ => quote!([(); #ch as _]), }); diff --git a/src/lib.rs b/src/lib.rs index d15d441..32d42a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,6 +345,7 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.3.0")] #![deny(improper_ctypes)] +#![allow(non_camel_case_types)] #![allow( clippy::cognitive_complexity, clippy::declare_interior_mutable_const, @@ -377,7 +378,6 @@ mod rust_sliceu8; mod rust_str; mod rust_string; mod rust_vec; -mod type_id; mod unique_ptr; mod unwind; @@ -403,7 +403,22 @@ pub mod private { pub use crate::rust_str::RustStr; pub use crate::rust_string::RustString; pub use crate::rust_vec::RustVec; - pub use crate::type_id::*; pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } + +macro_rules! chars { + ($($ch:ident)*) => { + $( + #[doc(hidden)] + pub enum $ch {} + )* + }; +} + +chars! { + _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + a b c d e f g h i j k l m n o p q r s t u v w x y z + __ // underscore +} diff --git a/src/type_id.rs b/src/type_id.rs deleted file mode 100644 index 68020ea..0000000 --- a/src/type_id.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![allow(non_camel_case_types)] - -macro_rules! chars { - ($($ch:ident)*) => { - $( - pub enum $ch {} - )* - }; -} - -chars! { - _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 - A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - a b c d e f g h i j k l m n o p q r s t u v w x y z - __ // underscore -} diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index e6b9fc7..165cdc0 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `::Id == (cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::B, cxx::type_id::y, cxx::type_id::t, cxx::type_id::e, cxx::type_id::R, cxx::type_id::a, cxx::type_id::n, cxx::type_id::g, cxx::type_id::e)` +error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` --> $DIR/wrong_type_id.rs:8:1 | 8 | #[cxx::bridge(namespace = folly)] @@ -9,6 +9,6 @@ error[E0271]: type mismatch resolving `, Id>() {} | ------- required by this bound in `cxx::extern_type::verify_extern_type` | - = note: expected tuple `(cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::B, cxx::type_id::y, cxx::type_id::t, cxx::type_id::e, cxx::type_id::R, cxx::type_id::a, cxx::type_id::n, cxx::type_id::g, cxx::type_id::e)` - found tuple `(cxx::type_id::f, cxx::type_id::o, cxx::type_id::l, cxx::type_id::l, cxx::type_id::y, (), cxx::type_id::S, cxx::type_id::t, cxx::type_id::r, cxx::type_id::i, cxx::type_id::n, cxx::type_id::g, cxx::type_id::P, cxx::type_id::i, cxx::type_id::e, cxx::type_id::c, cxx::type_id::e)` + = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From 83fe0f06d5933b324a288b8e1717b61dbf92ab9f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 03:14:49 +0000 Subject: [PATCH 566/2232] Preserve a better span for type id mismatch errors --- diff --git a/Cargo.toml b/Cargo.toml index 1455e55..dd7c18f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ cc = "1.0.49" cxx-build = { version = "=0.3.0", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = { version = "1.0.21", features = ["diff"] } +trybuild = { version = "1.0.27", features = ["diff"] } [workspace] members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e3026ba..efd20e8 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -575,8 +575,13 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { let ident = &alias.ident; let type_id = type_id(namespace, ident); + let begin_span = alias.type_token.span; + let end_span = alias.semi_token.span; + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); + let end = quote_spanned!(end_span=> >); + quote! { - const _: fn() = ::cxx::private::verify_extern_type::<#ident, #type_id>; + const _: fn() = #begin #ident, #type_id #end; } } diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 165cdc0..969c826 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,14 +1,13 @@ error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` - --> $DIR/wrong_type_id.rs:8:1 - | -8 | #[cxx::bridge(namespace = folly)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements - | - ::: $WORKSPACE/src/extern_type.rs:6:41 - | -6 | pub fn verify_extern_type, Id>() {} - | ------- required by this bound in `cxx::extern_type::verify_extern_type` - | - = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` - found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + --> $DIR/wrong_type_id.rs:11:9 + | +11 | type ByteRange = crate::here::StringPiece; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements + | + ::: $WORKSPACE/src/extern_type.rs:6:41 + | +6 | pub fn verify_extern_type, Id>() {} + | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | + = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` From e5a015a2b4c299aa77fd3d533290b916837ae8d9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 04:54:31 +0000 Subject: [PATCH 567/2232] Allow type alias as a method receiver --- diff --git a/syntax/parse.rs b/syntax/parse.rs index b41f3df..0640e7f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -193,11 +193,12 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec } let mut types = items.iter().filter_map(|item| match item { - Api::CxxType(ty) | Api::RustType(ty) => Some(ty), + Api::CxxType(ty) | Api::RustType(ty) => Some(&ty.ident), + Api::TypeAlias(alias) => Some(&alias.ident), _ => None, }); if let (Some(single_type), None) = (types.next(), types.next()) { - let single_type = single_type.ident.clone(); + let single_type = single_type.clone(); for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { From 2b821e60e00c139dd8a2db829c0bf5a052ebd875 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 06:18:09 +0000 Subject: [PATCH 568/2232] Don't do trait impls for non-local types --- diff --git a/gen/src/write.rs b/gen/src/write.rs index f28a58e..8f54bd7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -971,10 +971,6 @@ fn to_mangled(namespace: &Namespace, ty: &Type) -> String { } fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() - } - out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -991,14 +987,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if allow_unique_ptr(inner) { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_cxx_vector(out, ty, inner, types); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index efd20e8..fc129c6 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -77,13 +77,13 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. diff --git a/syntax/types.rs b/syntax/types.rs index 987d618..ba9bc78 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -12,6 +12,7 @@ pub struct Types<'a> { pub enums: Map, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, + pub aliases: Map, } impl<'a> Types<'a> { @@ -21,6 +22,7 @@ impl<'a> Types<'a> { let mut enums = Map::new(); let mut cxx = Set::new(); let mut rust = Set::new(); + let mut aliases = Map::new(); fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); @@ -102,6 +104,7 @@ impl<'a> Types<'a> { duplicate_name(cx, alias, ident); } cxx.insert(ident); + aliases.insert(ident.clone(), alias); } } } @@ -112,6 +115,7 @@ impl<'a> Types<'a> { enums, cxx, rust, + aliases, } } From a62cca2268582a42c9895428f71df5310ad6ad2a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 06:20:06 +0000 Subject: [PATCH 569/2232] Add a use of type alias to test suite --- diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index b970362..2c96d73 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,7 +3,8 @@ fn main() { return; } - cxx_build::bridge("lib.rs") + let sources = vec!["lib.rs", "module.rs"]; + cxx_build::bridges(sources) .file("tests.cc") .flag("-std=c++11") .compile("cxx-test-suite"); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e298e88..9457229 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,6 +4,8 @@ clippy::trivially_copy_pass_by_ref )] +pub mod module; + use cxx::{CxxString, UniquePtr}; use std::fmt::{self, Display}; @@ -47,7 +49,6 @@ pub mod ffi { fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); fn c_take_box(r: Box); - fn c_take_unique_ptr(c: UniquePtr); fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs new file mode 100644 index 0000000..77bae06 --- /dev/null +++ b/tests/ffi/module.rs @@ -0,0 +1,13 @@ +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi { + extern "C" { + include!("tests/ffi/tests.h"); + + type C = crate::ffi::C; + + fn c_take_unique_ptr(c: UniquePtr); + } +} diff --git a/tests/test.rs b/tests/test.rs index b8593c5..ddf8f70 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -89,7 +89,7 @@ fn test_c_take() { check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); - check!(ffi::c_take_unique_ptr(unique_ptr)); + check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); From 094db3ecea92f93463f7bea681a8fcb3c90bb5d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 09:06:30 +0000 Subject: [PATCH 570/2232] Document ExternType trait --- diff --git a/src/extern_type.rs b/src/extern_type.rs index f7c376b..6701ef5 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,4 +1,108 @@ +/// A type for which the layout is determined by its C++ definition. +/// +/// This trait serves the following two related purposes. +/// +///
+/// +/// ## Safely unifying occurrences of the same extern type +/// +/// `ExternType` makes it possible for CXX to safely share a consistent Rust +/// type across multiple #\[cxx::bridge\] invocations that refer to a common +/// extern C++ type. +/// +/// In the following snippet, two #\[cxx::bridge\] invocations in different +/// files (possibly different crates) both contain function signatures involving +/// the same C++ type `example::Demo`. If both were written just containing +/// `type Demo;`, then both macro expansions would produce their own separate +/// Rust type called `Demo` and thus the compiler wouldn't allow us to take the +/// `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo` +/// argument accepted by `file2::ffi::take_ref_demo`. Instead, one of the two +/// `Demo`s has been defined as an extern type alias of the other, making them +/// the same type in Rust. The CXX code generator will use an automatically +/// generated `ExternType` impl emitted in file1 to statically verify that in +/// file2 `crate::file1::ffi::Demo` really does refer to the C++ type +/// `example::Demo` as expected in file2. +/// +/// ```no_run +/// // file1.rs +/// # mod file1 { +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo; +/// +/// fn create_demo() -> UniquePtr; +/// } +/// } +/// # } +/// +/// // file2.rs +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo = crate::file1::ffi::Demo; +/// +/// fn take_ref_demo(demo: &Demo); +/// } +/// } +/// # +/// # fn main() {} +/// ``` +/// +///

+/// +/// ## Integrating with bindgen-generated types +/// +/// Handwritten `ExternType` impls make it possible to plug in a data structure +/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX. +/// +/// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ +/// namespace and type name given in the type id refers to a C++ type that is +/// equivalent to Rust type that is the `Self` type of the impl. +/// +/// ```no_run +/// # const _: &str = stringify! { +/// mod folly_sys; // the bindgen-generated bindings +/// # }; +/// # mod folly_sys { +/// # #[repr(transparent)] +/// # pub struct StringPiece([usize; 2]); +/// # } +/// +/// use cxx::{type_id, ExternType}; +/// +/// unsafe impl ExternType for folly_sys::StringPiece { +/// type Id = type_id!("folly::StringPiece"); +/// } +/// +/// #[cxx::bridge(namespace = folly)] +/// pub mod ffi { +/// extern "C" { +/// include!("rust_cxx_bindings.h"); +/// +/// type StringPiece = crate::folly_sys::StringPiece; +/// +/// fn print_string_piece(s: &StringPiece); +/// } +/// } +/// +/// // Now if we construct a StringPiece or obtain one through one +/// // of the bindgen-generated signatures, we are able to pass it +/// // along to ffi::print_string_piece. +/// # +/// # fn main() {} +/// ``` pub unsafe trait ExternType { + /// A type-level representation of the type's C++ namespace and type name. + /// + /// This will always be defined using `type_id!` in the following form: + /// + /// ``` + /// # struct TypeName; + /// # unsafe impl cxx::ExternType for TypeName { + /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// # } + /// ``` type Id; } diff --git a/src/lib.rs b/src/lib.rs index 32d42a0..fc0e682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -389,7 +389,10 @@ pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; -pub use cxxbridge_macro::{bridge, type_id}; +pub use cxxbridge_macro::{bridge}; + +/// For use in impls of the `ExternType` trait. See [`ExternType`]. +pub use cxxbridge_macro::type_id; // Not public API. #[doc(hidden)] diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 969c826..5b4f6c6 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,13 +1,13 @@ error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` - --> $DIR/wrong_type_id.rs:11:9 - | -11 | type ByteRange = crate::here::StringPiece; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements - | - ::: $WORKSPACE/src/extern_type.rs:6:41 - | -6 | pub fn verify_extern_type, Id>() {} - | ------- required by this bound in `cxx::extern_type::verify_extern_type` - | - = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` - found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` + --> $DIR/wrong_type_id.rs:11:9 + | +11 | type ByteRange = crate::here::StringPiece; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements + | + ::: $WORKSPACE/src/extern_type.rs:110:41 + | +110 | pub fn verify_extern_type, Id>() {} + | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | + = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` From ae4dedab556191d644f6c96c739962770d94eedf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 09:23:10 +0000 Subject: [PATCH 571/2232] Update build files of test suite --- diff --git a/tests/BUCK b/tests/BUCK index 659223c..41781c8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -6,7 +6,10 @@ rust_test( rust_library( name = "ffi", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], crate = "cxx_test_suite", deps = [ ":impl", @@ -18,25 +21,33 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], headers = { - "ffi/lib.rs.h": ":gen-header", + "ffi/lib.rs.h": ":gen-lib-header", "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - out = "generated.h", + out = "lib.rs.h", ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - out = "generated.cc", + out = "lib.rs.cc", +) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + out = "module.rs.cc", ) diff --git a/tests/BUILD b/tests/BUILD index 65c5b41..e1f1637 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -8,7 +8,10 @@ rust_test( rust_library( name = "cxx_test_suite", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], deps = [ ":impl", "//:cxx", @@ -19,17 +22,18 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], hdrs = ["ffi/tests.h"], deps = [ - ":include", + ":lib-include", "//:core", ], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], outs = ["lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -37,15 +41,23 @@ genrule( ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], - outs = ["generated.cc"], + outs = ["lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) cc_library( - name = "include", - hdrs = [":gen-header"], + name = "lib-include", + hdrs = [":gen-lib-header"], include_prefix = "tests/ffi", ) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + outs = ["module.rs.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 231863d..b1227f9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5696e4fd793743fbcc29943fe965ea3993b6c3d2a6a3a35c6680d926fd3a49" +checksum = "744665442556a91933cee5e75b0371376eb03498c4d0bfbcebd2a9882b4fb5ef" dependencies = [ "dissimilar", "glob", From 2429c6b0f6e8a203a05619e7990487a55b620cd1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 09:36:06 +0000 Subject: [PATCH 572/2232] Merge pull request #190 from dtolnay/id Enable sharing a consistent Rust type across multiple FFI blocks --- diff --git a/Cargo.toml b/Cargo.toml index 1455e55..dd7c18f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ cc = "1.0.49" cxx-build = { version = "=0.3.0", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = { version = "1.0.21", features = ["diff"] } +trybuild = { version = "1.0.27", features = ["diff"] } [workspace] members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 1cbcced..16aeba3 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,7 +13,7 @@ cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 57f2e39..92adb89 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" structopt = "0.3" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/src/write.rs b/gen/src/write.rs index f28a58e..8f54bd7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -971,10 +971,6 @@ fn to_mangled(namespace: &Namespace, ty: &Type) -> String { } fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() - } - out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -991,14 +987,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if allow_unique_ptr(inner) { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_cxx_vector(out, ty, inner, types); } diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4c2d423..8ade852 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -13,8 +13,8 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full"] } +quote = "1.0.4" +syn = { version = "1.0.19", features = ["full"] } [dev-dependencies] cxx = { version = "0.3", path = ".." } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fcb845c..fc129c6 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -3,7 +3,7 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -46,7 +46,7 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { if !types.enums.contains_key(&ety.ident) { - expanded.extend(expand_cxx_type(ety)); + expanded.extend(expand_cxx_type(namespace, ety)); } } Api::CxxFunction(efn) => { @@ -55,6 +55,10 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::RustFunction(efn) => { hidden.extend(expand_rust_function_shim(namespace, efn, types)) } + Api::TypeAlias(alias) => { + expanded.extend(expand_type_alias(alias)); + hidden.extend(expand_type_alias_verify(namespace, alias)); + } } } @@ -73,13 +77,13 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. @@ -161,15 +165,21 @@ fn expand_enum(enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(ety: &ExternType) -> TokenStream { +fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; + let type_id = type_id(namespace, ident); + quote! { #doc #[repr(C)] pub struct #ident { _private: ::cxx::private::Opaque, } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + } } } @@ -554,6 +564,40 @@ fn expand_rust_function_shim_impl( } } +fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let ty = &alias.ty; + quote! { + pub type #ident = #ty; + } +} + +fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let type_id = type_id(namespace, ident); + let begin_span = alias.type_token.span; + let end_span = alias.semi_token.span; + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); + let end = quote_spanned!(end_span=> >); + + quote! { + const _: fn() = #begin #ident, #type_id #end; + } +} + +fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { + let mut path = String::new(); + for name in namespace { + path += &name.to_string(); + path += "::"; + } + path += &ident.to_string(); + + quote! { + ::cxx::type_id!(#path) + } +} + fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b56f58e..bc8d19b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -11,10 +11,11 @@ extern crate proc_macro; mod expand; mod syntax; +mod type_id; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod}; +use syn::{parse_macro_input, ItemMod, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,3 +45,9 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +#[proc_macro] +pub fn type_id(input: TokenStream) -> TokenStream { + let arg = parse_macro_input!(input as LitStr); + type_id::expand(arg).into() +} diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs new file mode 100644 index 0000000..445da2b --- /dev/null +++ b/macro/src/type_id.rs @@ -0,0 +1,29 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::LitStr; + +// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` +pub fn expand(arg: LitStr) -> TokenStream { + let mut ids = Vec::new(); + + for word in arg.value().split("::") { + if !ids.is_empty() { + ids.push(quote!(())); + } + for ch in word.chars() { + ids.push(match ch { + 'A'..='Z' | 'a'..='z' => { + let t = format_ident!("{}", ch); + quote!(::cxx::#t) + } + '0'..='9' | '_' => { + let t = format_ident!("_{}", ch); + quote!(::cxx::#t) + } + _ => quote!([(); #ch as _]), + }); + } + } + + quote! { (#(#ids,)*) } +} diff --git a/src/extern_type.rs b/src/extern_type.rs new file mode 100644 index 0000000..6701ef5 --- /dev/null +++ b/src/extern_type.rs @@ -0,0 +1,110 @@ +/// A type for which the layout is determined by its C++ definition. +/// +/// This trait serves the following two related purposes. +/// +///
+/// +/// ## Safely unifying occurrences of the same extern type +/// +/// `ExternType` makes it possible for CXX to safely share a consistent Rust +/// type across multiple #\[cxx::bridge\] invocations that refer to a common +/// extern C++ type. +/// +/// In the following snippet, two #\[cxx::bridge\] invocations in different +/// files (possibly different crates) both contain function signatures involving +/// the same C++ type `example::Demo`. If both were written just containing +/// `type Demo;`, then both macro expansions would produce their own separate +/// Rust type called `Demo` and thus the compiler wouldn't allow us to take the +/// `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo` +/// argument accepted by `file2::ffi::take_ref_demo`. Instead, one of the two +/// `Demo`s has been defined as an extern type alias of the other, making them +/// the same type in Rust. The CXX code generator will use an automatically +/// generated `ExternType` impl emitted in file1 to statically verify that in +/// file2 `crate::file1::ffi::Demo` really does refer to the C++ type +/// `example::Demo` as expected in file2. +/// +/// ```no_run +/// // file1.rs +/// # mod file1 { +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo; +/// +/// fn create_demo() -> UniquePtr; +/// } +/// } +/// # } +/// +/// // file2.rs +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo = crate::file1::ffi::Demo; +/// +/// fn take_ref_demo(demo: &Demo); +/// } +/// } +/// # +/// # fn main() {} +/// ``` +/// +///

+/// +/// ## Integrating with bindgen-generated types +/// +/// Handwritten `ExternType` impls make it possible to plug in a data structure +/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX. +/// +/// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ +/// namespace and type name given in the type id refers to a C++ type that is +/// equivalent to Rust type that is the `Self` type of the impl. +/// +/// ```no_run +/// # const _: &str = stringify! { +/// mod folly_sys; // the bindgen-generated bindings +/// # }; +/// # mod folly_sys { +/// # #[repr(transparent)] +/// # pub struct StringPiece([usize; 2]); +/// # } +/// +/// use cxx::{type_id, ExternType}; +/// +/// unsafe impl ExternType for folly_sys::StringPiece { +/// type Id = type_id!("folly::StringPiece"); +/// } +/// +/// #[cxx::bridge(namespace = folly)] +/// pub mod ffi { +/// extern "C" { +/// include!("rust_cxx_bindings.h"); +/// +/// type StringPiece = crate::folly_sys::StringPiece; +/// +/// fn print_string_piece(s: &StringPiece); +/// } +/// } +/// +/// // Now if we construct a StringPiece or obtain one through one +/// // of the bindgen-generated signatures, we are able to pass it +/// // along to ffi::print_string_piece. +/// # +/// # fn main() {} +/// ``` +pub unsafe trait ExternType { + /// A type-level representation of the type's C++ namespace and type name. + /// + /// This will always be defined using `type_id!` in the following form: + /// + /// ``` + /// # struct TypeName; + /// # unsafe impl cxx::ExternType for TypeName { + /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// # } + /// ``` + type Id; +} + +#[doc(hidden)] +pub fn verify_extern_type, Id>() {} diff --git a/src/lib.rs b/src/lib.rs index cc0dcf2..fc0e682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,6 +345,7 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.3.0")] #![deny(improper_ctypes)] +#![allow(non_camel_case_types)] #![allow( clippy::cognitive_complexity, clippy::declare_interior_mutable_const, @@ -369,6 +370,7 @@ mod macros; mod cxx_string; mod cxx_vector; mod exception; +mod extern_type; mod function; mod opaque; mod result; @@ -385,13 +387,18 @@ mod symbols; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; +pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; -pub use cxxbridge_macro::bridge; +pub use cxxbridge_macro::{bridge}; + +/// For use in impls of the `ExternType` trait. See [`ExternType`]. +pub use cxxbridge_macro::type_id; // Not public API. #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; + pub use crate::extern_type::verify_extern_type; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; @@ -402,3 +409,19 @@ pub mod private { pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } + +macro_rules! chars { + ($($ch:ident)*) => { + $( + #[doc(hidden)] + pub enum $ch {} + )* + }; +} + +chars! { + _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + a b c d e f g h i j k l m n o p q r s t u v w x y z + __ // underscore +} diff --git a/syntax/ident.rs b/syntax/ident.rs index cec424c..7545e92 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -42,6 +42,9 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &arg.ident); } } + Api::TypeAlias(alias) => { + check(cx, &alias.ident); + } } } } diff --git a/syntax/mod.rs b/syntax/mod.rs index fd8db73..3eb6c6e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -20,7 +20,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Lifetime, LitStr, Token}; +use syn::{Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -35,6 +35,7 @@ pub enum Api { CxxFunction(ExternFn), RustType(ExternType), RustFunction(ExternFn), + TypeAlias(TypeAlias), } pub struct ExternType { @@ -68,6 +69,14 @@ pub struct ExternFn { pub semi_token: Token![;], } +pub struct TypeAlias { + pub type_token: Token![type], + pub ident: Ident, + pub eq_token: Token![=], + pub ty: RustType, + pub semi_token: Token![;], +} + pub struct Signature { pub fn_token: Token![fn], pub receiver: Option, diff --git a/syntax/parse.rs b/syntax/parse.rs index b794ba9..0640e7f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -2,17 +2,19 @@ use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, Var, Variant, + Struct, Ty1, Type, TypeAlias, Var, Variant, }; +use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::HashSet; use std::u32; +use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, - Variant as RustVariant, + Abi, Attribute, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, + ForeignItemType, GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, + PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, + TypeReference, TypeSlice, Variant as RustVariant, }; pub mod kw { @@ -182,16 +184,21 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), } } + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(tokens, lang) { + Ok(api) => items.push(api), + Err(err) => cx.push(err), + }, _ => cx.error(foreign, "unsupported foreign item"), } } let mut types = items.iter().filter_map(|item| match item { - Api::CxxType(ty) | Api::RustType(ty) => Some(ty), + Api::CxxType(ty) | Api::RustType(ty) => Some(&ty.ident), + Api::TypeAlias(alias) => Some(&alias.ident), _ => None, }); if let (Some(single_type), None) = (types.next(), types.next()) { - let single_type = single_type.ident.clone(); + let single_type = single_type.clone(); for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { @@ -336,6 +343,44 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { })) } +fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { + // type Alias = crate::path::to::Type; + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + let type_token: Token![type] = match input.parse()? { + Some(type_token) => type_token, + None => { + let span = input.cursor().token_stream(); + return Err(Error::new_spanned(span, "unsupported foreign item")); + } + }; + let ident: Ident = input.parse()?; + let eq_token: Token![=] = input.parse()?; + let ty: RustType = input.parse()?; + let semi_token: Token![;] = input.parse()?; + attrs::parse_doc(&attrs)?; + + Ok(TypeAlias { + type_token, + ident, + eq_token, + ty, + semi_token, + }) + } + + let type_alias = parse.parse2(tokens.clone())?; + match lang { + Lang::Cxx => Ok(Api::TypeAlias(type_alias)), + Lang::Rust => { + let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token); + let span = quote!(#type_token #semi_token); + let msg = "type alias in extern \"Rust\" block is not supported"; + Err(Error::new_spanned(span, msg)) + } + } +} + fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 934c85b..6dd6073 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, Var, + Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, + TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -86,6 +87,14 @@ impl ToTokens for ExternType { } } +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.ident.to_tokens(tokens); + } +} + impl ToTokens for Struct { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/syntax/types.rs b/syntax/types.rs index 5800513..ba9bc78 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -12,6 +12,7 @@ pub struct Types<'a> { pub enums: Map, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, + pub aliases: Map, } impl<'a> Types<'a> { @@ -21,6 +22,7 @@ impl<'a> Types<'a> { let mut enums = Map::new(); let mut cxx = Set::new(); let mut rust = Set::new(); + let mut aliases = Map::new(); fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); @@ -96,6 +98,14 @@ impl<'a> Types<'a> { visit(&mut all, ret); } } + Api::TypeAlias(alias) => { + let ident = &alias.ident; + if !type_names.insert(ident) { + duplicate_name(cx, alias, ident); + } + cxx.insert(ident); + aliases.insert(ident.clone(), alias); + } } } @@ -105,6 +115,7 @@ impl<'a> Types<'a> { enums, cxx, rust, + aliases, } } diff --git a/tests/BUCK b/tests/BUCK index 659223c..41781c8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -6,7 +6,10 @@ rust_test( rust_library( name = "ffi", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], crate = "cxx_test_suite", deps = [ ":impl", @@ -18,25 +21,33 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], headers = { - "ffi/lib.rs.h": ":gen-header", + "ffi/lib.rs.h": ":gen-lib-header", "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - out = "generated.h", + out = "lib.rs.h", ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - out = "generated.cc", + out = "lib.rs.cc", +) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + out = "module.rs.cc", ) diff --git a/tests/BUILD b/tests/BUILD index 65c5b41..e1f1637 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -8,7 +8,10 @@ rust_test( rust_library( name = "cxx_test_suite", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], deps = [ ":impl", "//:cxx", @@ -19,17 +22,18 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], hdrs = ["ffi/tests.h"], deps = [ - ":include", + ":lib-include", "//:core", ], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], outs = ["lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -37,15 +41,23 @@ genrule( ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], - outs = ["generated.cc"], + outs = ["lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) cc_library( - name = "include", - hdrs = [":gen-header"], + name = "lib-include", + hdrs = [":gen-lib-header"], include_prefix = "tests/ffi", ) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + outs = ["module.rs.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index b970362..2c96d73 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,7 +3,8 @@ fn main() { return; } - cxx_build::bridge("lib.rs") + let sources = vec!["lib.rs", "module.rs"]; + cxx_build::bridges(sources) .file("tests.cc") .flag("-std=c++11") .compile("cxx-test-suite"); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e298e88..9457229 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,6 +4,8 @@ clippy::trivially_copy_pass_by_ref )] +pub mod module; + use cxx::{CxxString, UniquePtr}; use std::fmt::{self, Display}; @@ -47,7 +49,6 @@ pub mod ffi { fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); fn c_take_box(r: Box); - fn c_take_unique_ptr(c: UniquePtr); fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs new file mode 100644 index 0000000..77bae06 --- /dev/null +++ b/tests/ffi/module.rs @@ -0,0 +1,13 @@ +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi { + extern "C" { + include!("tests/ffi/tests.h"); + + type C = crate::ffi::C; + + fn c_take_unique_ptr(c: UniquePtr); + } +} diff --git a/tests/test.rs b/tests/test.rs index b8593c5..ddf8f70 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -89,7 +89,7 @@ fn test_c_take() { check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); - check!(ffi::c_take_unique_ptr(unique_ptr)); + check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); diff --git a/tests/ui/type_alias_rust.rs b/tests/ui/type_alias_rust.rs new file mode 100644 index 0000000..67df489 --- /dev/null +++ b/tests/ui/type_alias_rust.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + extern "Rust" { + /// Incorrect. + type Alias = crate::Type; + } +} + +fn main() {} diff --git a/tests/ui/type_alias_rust.stderr b/tests/ui/type_alias_rust.stderr new file mode 100644 index 0000000..1b08f67 --- /dev/null +++ b/tests/ui/type_alias_rust.stderr @@ -0,0 +1,5 @@ +error: type alias in extern "Rust" block is not supported + --> $DIR/type_alias_rust.rs:5:9 + | +5 | type Alias = crate::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs new file mode 100644 index 0000000..81a9b3f --- /dev/null +++ b/tests/ui/wrong_type_id.rs @@ -0,0 +1,15 @@ +#[cxx::bridge(namespace = folly)] +mod here { + extern "C" { + type StringPiece; + } +} + +#[cxx::bridge(namespace = folly)] +mod there { + extern "C" { + type ByteRange = crate::here::StringPiece; + } +} + +fn main() {} diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr new file mode 100644 index 0000000..5b4f6c6 --- /dev/null +++ b/tests/ui/wrong_type_id.stderr @@ -0,0 +1,13 @@ +error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + --> $DIR/wrong_type_id.rs:11:9 + | +11 | type ByteRange = crate::here::StringPiece; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements + | + ::: $WORKSPACE/src/extern_type.rs:110:41 + | +110 | pub fn verify_extern_type, Id>() {} + | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | + = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` diff --git a/third-party/BUCK b/third-party/BUCK index 2de784b..e16bad2 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 4d8fd9f..45b1163 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3397192..b1227f9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" dependencies = [ "proc-macro2", "quote", @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5696e4fd793743fbcc29943fe965ea3993b6c3d2a6a3a35c6680d926fd3a49" +checksum = "744665442556a91933cee5e75b0371376eb03498c4d0bfbcebd2a9882b4fb5ef" dependencies = [ "dissimilar", "glob", From 878ab12a155a613e9a2a41304d260fe4fdba7f2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 17:05:03 +0000 Subject: [PATCH 573/2232] Release 0.3.1 --- diff --git a/Cargo.toml b/Cargo.toml index dd7c18f..09280f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.0" # remember to update html_root_url +version = "0.3.1" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -12,14 +12,14 @@ readme = "README.md" exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] [dependencies] -cxxbridge-macro = { version = "=0.3.0", path = "macro" } +cxxbridge-macro = { version = "=0.3.1", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.0", path = "gen/build" } +cxx-build = { version = "=0.3.1", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.27", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 16aeba3..4f4db9d 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.0" +version = "0.3.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 92adb89..bb2c72d 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.0" +version = "0.3.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 8ade852..3b9662b 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.0" +version = "0.3.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index fc0e682..43540be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,7 +343,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.0")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.1")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b1227f9..e95d00c 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.0" +version = "0.3.1" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "codespan-reporting", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.0" +version = "0.3.1" dependencies = [ "cxx", "proc-macro2", @@ -389,9 +389,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" From d24563dd24db07bb08b92c22cd8f4c820d5ffc9f Mon Sep 17 00:00:00 2001 From: Christopher Chalmers Date: May 08 2020 19:39:10 +0000 Subject: [PATCH 574/2232] Add cpp flag to cc This means that clang++ will be called instead of clang. --- diff --git a/build.rs b/build.rs index 1f3b6eb..05d7880 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,7 @@ fn main() { cc::Build::new() .file("src/cxx.cc") + .cpp(true) .flag("-std=c++11") .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); diff --git a/demo-rs/build.rs b/demo-rs/build.rs index edbb281..e435019 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,6 +1,7 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") + .cpp(true) .flag("-std=c++11") .compile("cxxbridge-demo"); From 96c351b5a845bb01f3df528f0ff893ed0377eef7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 20:06:18 +0000 Subject: [PATCH 575/2232] Format with rustfmt 2020-04-14 --- diff --git a/src/lib.rs b/src/lib.rs index 43540be..2da7a59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -389,7 +389,7 @@ pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; -pub use cxxbridge_macro::{bridge}; +pub use cxxbridge_macro::bridge; /// For use in impls of the `ExternType` trait. See [`ExternType`]. pub use cxxbridge_macro::type_id; From 110df7dc214a1a92c930434be90150cba250b38f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 20:09:56 +0000 Subject: [PATCH 576/2232] Defer to link-cplusplus crate for linking a standard library --- diff --git a/build.rs b/build.rs index 05d7880..16837f3 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,7 @@ fn main() { cc::Build::new() .file("src/cxx.cc") .cpp(true) + .cpp_link_stdlib(None) // linked via link-cplusplus crate .flag("-std=c++11") .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); diff --git a/demo-rs/build.rs b/demo-rs/build.rs index e435019..edbb281 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,7 +1,6 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") - .cpp(true) .flag("-std=c++11") .compile("cxxbridge-demo"); diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 9e8475a..b33e192 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -88,12 +88,16 @@ pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { /// ``` pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { let mut build = paths::cc_build(); + build.cpp(true); + build.cpp_link_stdlib(None); // linked via link-cplusplus crate + for path in rust_source_files { if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); process::exit(1); } } + build } From 887c8a2eb1de93737173b9f26fc79788319265fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 22:55:10 +0000 Subject: [PATCH 577/2232] Merge pull request #192 from cchalmers/cpp-true Add cpp flag to cc --- diff --git a/build.rs b/build.rs index 1f3b6eb..16837f3 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,8 @@ fn main() { cc::Build::new() .file("src/cxx.cc") + .cpp(true) + .cpp_link_stdlib(None) // linked via link-cplusplus crate .flag("-std=c++11") .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 9e8475a..b33e192 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -88,12 +88,16 @@ pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { /// ``` pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { let mut build = paths::cc_build(); + build.cpp(true); + build.cpp_link_stdlib(None); // linked via link-cplusplus crate + for path in rust_source_files { if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); process::exit(1); } } + build } From e2f70fea3e769d4c135ad8808983b9939928b172 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 08 2020 22:57:14 +0000 Subject: [PATCH 578/2232] Release 0.3.2 --- diff --git a/Cargo.toml b/Cargo.toml index 09280f4..32e5803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.1" # remember to update html_root_url +version = "0.3.2" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -12,14 +12,14 @@ readme = "README.md" exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] [dependencies] -cxxbridge-macro = { version = "=0.3.1", path = "macro" } +cxxbridge-macro = { version = "=0.3.2", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.1", path = "gen/build" } +cxx-build = { version = "=0.3.2", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.27", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 4f4db9d..71c6aef 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.1" +version = "0.3.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index bb2c72d..2ce8ce5 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.1" +version = "0.3.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 3b9662b..bc3f001 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.1" +version = "0.3.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 2da7a59..78aba3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,7 +343,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.1")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.2")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index e95d00c..e54c293 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.1" +version = "0.3.2" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "codespan-reporting", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.1" +version = "0.3.2" dependencies = [ "cxx", "proc-macro2", @@ -246,18 +246,18 @@ checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "serde" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "eba7550f2cdf88ffc23ab0f1607133486c390a8c0f89b57e589b9654ee15e04d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" dependencies = [ "proc-macro2", "quote", From 064668a00f5c3965ef52fc405b07745a321b92c8 Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: May 09 2020 07:24:12 +0000 Subject: [PATCH 579/2232] fix path to c++ generator example --- diff --git a/README.md b/README.md index 1b1611d..04cabd0 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ generators: $ cargo expand --manifest-path demo-rs/Cargo.toml # run C++ code generator and print to stdout -$ cargo run --manifest-path cmd/Cargo.toml -- demo-rs/src/main.rs +$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs ```
From 55f4949549cf46046091a40248cc97e37010fbd2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 09 2020 16:56:08 +0000 Subject: [PATCH 580/2232] Merge pull request #193 from philipcraig/fix_readme fix path to c++ generator example --- diff --git a/README.md b/README.md index 1b1611d..04cabd0 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ generators: $ cargo expand --manifest-path demo-rs/Cargo.toml # run C++ code generator and print to stdout -$ cargo run --manifest-path cmd/Cargo.toml -- demo-rs/src/main.rs +$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs ```
From 32439dae389e4439c7e8e1d4bd650765c23854e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 09 2020 16:59:40 +0000 Subject: [PATCH 581/2232] Apply PR 193 to rustdoc as well --- diff --git a/src/lib.rs b/src/lib.rs index 78aba3d..bbe66f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,7 +122,7 @@ //! $ cargo expand --manifest-path demo-rs/Cargo.toml //! //! # run C++ code generator and print to stdout -//! $ cargo run --manifest-path cmd/Cargo.toml -- demo-rs/src/main.rs +//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs //! ``` //! //!
From 7e14e2e6cbab8d1c556b556983e2a5e2dcbba6cc Mon Sep 17 00:00:00 2001 From: Philip Craig <689193+philipcraig@users.noreply.github.com> Date: May 09 2020 19:00:49 +0000 Subject: [PATCH 582/2232] avoid MSVC warning via flag_if_supported --- diff --git a/README.md b/README.md index 1b1611d..f819636 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ cxx-build = "0.3" fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build .file("../demo-cxx/demo.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/build.rs b/build.rs index 16837f3..9a071fe 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); diff --git a/demo-rs/build.rs b/demo-rs/build.rs index edbb281..e4792c2 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,7 +1,7 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index b33e192..14683b6 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -16,7 +16,7 @@ //! fn main() { //! cxx_build::bridge("src/main.rs") //! .file("../demo-cxx/demo.cc") -//! .flag("-std=c++11") +//! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); @@ -83,7 +83,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) /// .file("../demo-cxx/demo.cc") -/// .flag("-std=c++11") +/// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { diff --git a/src/lib.rs b/src/lib.rs index 78aba3d..7e6ece4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! fn main() { //! cxx_build::bridge("src/main.rs") // returns a cc::Build //! .file("../demo-cxx/demo.cc") -//! .flag("-std=c++11") +//! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 2c96d73..f6fa59e 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,6 +6,6 @@ fn main() { let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxx-test-suite"); } From 6cadf70b5833f511e3cc902e6ddbe10ab620f447 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 09 2020 19:06:54 +0000 Subject: [PATCH 583/2232] Merge pull request #194 from philipcraig/std_c++11_not_required_on_msvc avoid MSVC warning via flag_if_supported --- diff --git a/README.md b/README.md index 04cabd0..9e397f2 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ cxx-build = "0.3" fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build .file("../demo-cxx/demo.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/build.rs b/build.rs index 16837f3..9a071fe 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); diff --git a/demo-rs/build.rs b/demo-rs/build.rs index edbb281..e4792c2 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,7 +1,7 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index b33e192..14683b6 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -16,7 +16,7 @@ //! fn main() { //! cxx_build::bridge("src/main.rs") //! .file("../demo-cxx/demo.cc") -//! .flag("-std=c++11") +//! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); @@ -83,7 +83,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) /// .file("../demo-cxx/demo.cc") -/// .flag("-std=c++11") +/// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { diff --git a/src/lib.rs b/src/lib.rs index bbe66f9..3b37c5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! fn main() { //! cxx_build::bridge("src/main.rs") // returns a cc::Build //! .file("../demo-cxx/demo.cc") -//! .flag("-std=c++11") +//! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 2c96d73..f6fa59e 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,6 +6,6 @@ fn main() { let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") - .flag("-std=c++11") + .flag_if_supported("-std=c++11") .compile("cxx-test-suite"); } From e86b9cf2259dab6fe9f61d4efc85ce379c9bea63 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 10 2020 21:26:04 +0000 Subject: [PATCH 584/2232] Update derive parsing to produce structured representation --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 6d661ba..798cbee 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -13,7 +13,7 @@ pub(super) fn parse_doc(attrs: &[Attribute]) -> Result { pub(super) fn parse( attrs: &[Attribute], doc: &mut Doc, - mut derives: Option<&mut Vec>, + mut derives: Option<&mut Vec>, ) -> Result<()> { for attr in attrs { if attr.path.is_ident("doc") { @@ -37,13 +37,17 @@ fn parse_doc_attribute(input: ParseStream) -> Result { Ok(lit) } -fn parse_derive_attribute(input: ParseStream) -> Result> { +fn parse_derive_attribute(input: ParseStream) -> Result> { input .parse_terminated::(Path::parse_mod_style)? .into_iter() - .map(|path| match path.get_ident() { - Some(ident) if Derive::from(ident).is_some() => Ok(ident.clone()), - _ => Err(Error::new_spanned(path, "unsupported derive")), + .map(|path| { + if let Some(ident) = path.get_ident() { + if let Some(derive) = Derive::from(ident) { + return Ok(derive); + } + } + Err(Error::new_spanned(path, "unsupported derive")) }) .collect() } diff --git a/syntax/mod.rs b/syntax/mod.rs index 3eb6c6e..9e4cbbc 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -46,7 +46,7 @@ pub struct ExternType { pub struct Struct { pub doc: Doc, - pub derives: Vec, + pub derives: Vec, pub struct_token: Token![struct], pub ident: Ident, pub brace_token: Brace, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 6dd6073..9a1fee0 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -71,11 +71,7 @@ impl ToTokens for Slice { impl ToTokens for Derive { fn to_tokens(&self, tokens: &mut TokenStream) { - let name = match self { - Derive::Clone => "Clone", - Derive::Copy => "Copy", - }; - Ident::new(name, Span::call_site()).to_tokens(tokens); + Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens); } } From b129ea71633fea7f6a969ba428cae755e676ee07 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 10 2020 21:48:30 +0000 Subject: [PATCH 585/2232] Organize how the caller determines which attrs to parse In preparation for parsing even more attributes, such as `repr`. --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 798cbee..edb403d 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,27 +1,36 @@ use crate::syntax::{Derive, Doc}; use proc_macro2::Ident; -use syn::parse::{ParseStream, Parser}; +use syn::parse::{ParseStream, Parser as _}; use syn::{Attribute, Error, LitStr, Path, Result, Token}; +#[derive(Default)] +pub struct Parser<'a> { + pub doc: Option<&'a mut Doc>, + pub derives: Option<&'a mut Vec>, +} + pub(super) fn parse_doc(attrs: &[Attribute]) -> Result { let mut doc = Doc::new(); - let derives = None; - parse(attrs, &mut doc, derives)?; + parse( + attrs, + Parser { + doc: Some(&mut doc), + ..Parser::default() + }, + )?; Ok(doc) } -pub(super) fn parse( - attrs: &[Attribute], - doc: &mut Doc, - mut derives: Option<&mut Vec>, -) -> Result<()> { +pub(super) fn parse(attrs: &[Attribute], mut parser: Parser) -> Result<()> { for attr in attrs { if attr.path.is_ident("doc") { - let lit = parse_doc_attribute.parse2(attr.tokens.clone())?; - doc.push(lit); - continue; + if let Some(doc) = &mut parser.doc { + let lit = parse_doc_attribute.parse2(attr.tokens.clone())?; + doc.push(lit); + continue; + } } else if attr.path.is_ident("derive") { - if let Some(derives) = &mut derives { + if let Some(derives) = &mut parser.derives { derives.extend(attr.parse_args_with(parse_derive_attribute)?); continue; } diff --git a/syntax/parse.rs b/syntax/parse.rs index 0640e7f..b5070c7 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -56,7 +56,13 @@ fn parse_struct(item: ItemStruct) -> Result { let mut doc = Doc::new(); let mut derives = Vec::new(); - attrs::parse(&item.attrs, &mut doc, Some(&mut derives))?; + attrs::parse( + &item.attrs, + attrs::Parser { + doc: Some(&mut doc), + derives: Some(&mut derives), + }, + )?; let fields = match item.fields { Fields::Named(fields) => fields, From 3e6288896061b170d2dfa72d7b009915d1269641 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 10 2020 22:36:01 +0000 Subject: [PATCH 586/2232] Keep items that have attr parse errors --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index edb403d..1cb0657 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,3 +1,4 @@ +use crate::syntax::report::Errors; use crate::syntax::{Derive, Doc}; use proc_macro2::Ident; use syn::parse::{ParseStream, Parser as _}; @@ -9,35 +10,44 @@ pub struct Parser<'a> { pub derives: Option<&'a mut Vec>, } -pub(super) fn parse_doc(attrs: &[Attribute]) -> Result { +pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { let mut doc = Doc::new(); parse( + cx, attrs, Parser { doc: Some(&mut doc), ..Parser::default() }, - )?; - Ok(doc) + ); + doc } -pub(super) fn parse(attrs: &[Attribute], mut parser: Parser) -> Result<()> { +pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { for attr in attrs { if attr.path.is_ident("doc") { - if let Some(doc) = &mut parser.doc { - let lit = parse_doc_attribute.parse2(attr.tokens.clone())?; - doc.push(lit); - continue; + match parse_doc_attribute.parse2(attr.tokens.clone()) { + Ok(lit) => { + if let Some(doc) = &mut parser.doc { + doc.push(lit); + continue; + } + } + Err(err) => return cx.push(err), } } else if attr.path.is_ident("derive") { - if let Some(derives) = &mut parser.derives { - derives.extend(attr.parse_args_with(parse_derive_attribute)?); - continue; + match attr.parse_args_with(parse_derive_attribute) { + Ok(attr) => { + if let Some(derives) = &mut parser.derives { + derives.extend(attr); + continue; + } + } + Err(err) => return cx.push(err), } } - return Err(Error::new_spanned(attr, "unsupported attribute")); + return cx.error(attr, "unsupported attribute"); } - Ok(()) } fn parse_doc_attribute(input: ParseStream) -> Result { diff --git a/syntax/parse.rs b/syntax/parse.rs index b5070c7..f84ff32 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -25,11 +25,11 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(item) { + Item::Struct(item) => match parse_struct(cx, item) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(item) { + Item::Enum(item) => match parse_enum(cx, item) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, @@ -41,7 +41,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { apis } -fn parse_struct(item: ItemStruct) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -57,12 +57,13 @@ fn parse_struct(item: ItemStruct) -> Result { let mut doc = Doc::new(); let mut derives = Vec::new(); attrs::parse( + cx, &item.attrs, attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), }, - )?; + ); let fields = match item.fields { Fields::Named(fields) => fields, @@ -91,7 +92,7 @@ fn parse_struct(item: ItemStruct) -> Result { })) } -fn parse_enum(item: ItemEnum) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -104,7 +105,7 @@ fn parse_enum(item: ItemEnum) -> Result { )); } - let doc = attrs::parse_doc(&item.attrs)?; + let doc = attrs::parse_doc(cx, &item.attrs); let mut variants = Vec::new(); let mut discriminants = HashSet::new(); @@ -176,11 +177,11 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(foreign, lang) { + ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang) { Ok(ety) => items.push(ety), Err(err) => cx.push(err), }, - ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) { + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -190,7 +191,7 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), } } - ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(tokens, lang) { + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) { Ok(api) => items.push(api), Err(err) => cx.push(err), }, @@ -236,8 +237,8 @@ fn parse_lang(abi: Abi) -> Result { } } -fn parse_extern_type(foreign_type: &ForeignItemType, lang: Lang) -> Result { - let doc = attrs::parse_doc(&foreign_type.attrs)?; +fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang) -> Result { + let doc = attrs::parse_doc(cx, &foreign_type.attrs); let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); let api_type = match lang { @@ -251,7 +252,7 @@ fn parse_extern_type(foreign_type: &ForeignItemType, lang: Lang) -> Result })) } -fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -322,7 +323,7 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { let mut throws_tokens = None; let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); - let doc = attrs::parse_doc(&foreign_fn.attrs)?; + let doc = attrs::parse_doc(cx, &foreign_fn.attrs); let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); let paren_token = foreign_fn.sig.paren_token; @@ -349,9 +350,9 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { })) } -fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { +fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { // type Alias = crate::path::to::Type; - fn parse(input: ParseStream) -> Result { + let parse = |input: ParseStream| -> Result { let attrs = input.call(Attribute::parse_outer)?; let type_token: Token![type] = match input.parse()? { Some(type_token) => type_token, @@ -364,7 +365,7 @@ fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - attrs::parse_doc(&attrs)?; + attrs::parse_doc(cx, &attrs); Ok(TypeAlias { type_token, @@ -373,7 +374,7 @@ fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { ty, semi_token, }) - } + }; let type_alias = parse.parse2(tokens.clone())?; match lang { From c0fad20670d85aad0cd560dce126fd7a52cbece7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 03:16:28 +0000 Subject: [PATCH 587/2232] Add crates.io categories and keywords --- diff --git a/Cargo.toml b/Cargo.toml index 32e5803..829eac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ repository = "https://github.com/dtolnay/cxx" documentation = "https://docs.rs/cxx" readme = "README.md" exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] +keywords = ["ffi"] +categories = ["development-tools::ffi", "api-bindings"] [dependencies] cxxbridge-macro = { version = "=0.3.2", path = "macro" } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 71c6aef..84ab6fe 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -6,6 +6,8 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a Cargo build." repository = "https://github.com/dtolnay/cxx" +keywords = ["ffi"] +categories = ["development-tools::ffi"] [dependencies] anyhow = "1.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 2ce8ce5..d14dc84 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -6,6 +6,8 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." repository = "https://github.com/dtolnay/cxx" +keywords = ["ffi"] +categories = ["development-tools::ffi"] [[bin]] name = "cxxbridge" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index bc3f001..0dc3038 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -7,6 +7,8 @@ license = "MIT OR Apache-2.0" description = "Implementation detail of the `cxx` crate." repository = "https://github.com/dtolnay/cxx" exclude = ["README.md"] +keywords = ["ffi"] +categories = ["development-tools::ffi"] [lib] proc-macro = true From 64703b42071b90875501abef44e31ee83be48721 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:12:33 +0000 Subject: [PATCH 588/2232] Give Derive enum and impls their own module --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 1cb0657..f48a210 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,6 +1,5 @@ use crate::syntax::report::Errors; use crate::syntax::{Derive, Doc}; -use proc_macro2::Ident; use syn::parse::{ParseStream, Parser as _}; use syn::{Attribute, Error, LitStr, Path, Result, Token}; @@ -70,22 +69,3 @@ fn parse_derive_attribute(input: ParseStream) -> Result> { }) .collect() } - -impl Derive { - pub fn from(ident: &Ident) -> Option { - match ident.to_string().as_str() { - "Clone" => Some(Derive::Clone), - "Copy" => Some(Derive::Copy), - _ => None, - } - } -} - -impl AsRef for Derive { - fn as_ref(&self) -> &str { - match self { - Derive::Clone => "Clone", - Derive::Copy => "Copy", - } - } -} diff --git a/syntax/derive.rs b/syntax/derive.rs new file mode 100644 index 0000000..435aa20 --- /dev/null +++ b/syntax/derive.rs @@ -0,0 +1,26 @@ +use proc_macro2::Ident; + +#[derive(Copy, Clone, PartialEq)] +pub enum Derive { + Clone, + Copy, +} + +impl Derive { + pub fn from(ident: &Ident) -> Option { + match ident.to_string().as_str() { + "Clone" => Some(Derive::Clone), + "Copy" => Some(Derive::Copy), + _ => None, + } + } +} + +impl AsRef for Derive { + fn as_ref(&self) -> &str { + match self { + Derive::Clone => "Clone", + Derive::Copy => "Copy", + } + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index 9e4cbbc..d21c083 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -3,6 +3,7 @@ pub mod atom; mod attrs; pub mod check; +mod derive; mod doc; pub mod error; pub mod ident; @@ -23,6 +24,7 @@ use syn::token::{Brace, Bracket, Paren}; use syn::{Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; +pub use self::derive::Derive; pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; @@ -145,9 +147,3 @@ pub enum Lang { Cxx, Rust, } - -#[derive(Copy, Clone, PartialEq)] -pub enum Derive { - Clone, - Copy, -} From a4596c4455ab7ddc2386182e25d7b65358c2bd64 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:55:45 +0000 Subject: [PATCH 589/2232] Expose Atom str for use in error messages --- diff --git a/syntax/atom.rs b/syntax/atom.rs index eeea831..5b20c4c 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -44,9 +44,26 @@ impl Atom { } } -impl PartialEq for Ident { - fn eq(&self, atom: &Atom) -> bool { - Atom::from(self) == Some(*atom) +impl AsRef for Atom { + fn as_ref(&self) -> &str { + use self::Atom::*; + match self { + Bool => "bool", + 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", + } } } From 699351bc78c30bfcf1b57da62e6d25fc35b0cdeb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:08 +0000 Subject: [PATCH 590/2232] Display for Atom --- diff --git a/syntax/atom.rs b/syntax/atom.rs index 5b20c4c..a2ff7b7 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -1,5 +1,6 @@ use crate::syntax::Type; use proc_macro2::Ident; +use std::fmt::{self, Display}; #[derive(Copy, Clone, PartialEq)] pub enum Atom { @@ -44,6 +45,12 @@ impl Atom { } } +impl Display for Atom { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(self.as_ref()) + } +} + impl AsRef for Atom { fn as_ref(&self) -> &str { use self::Atom::*; From 17e137fbb3f02a1c41c0124ae53da89ae83d499e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 591/2232] Factor out a discriminant processing library --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs new file mode 100644 index 0000000..2a55468 --- /dev/null +++ b/syntax/discriminant.rs @@ -0,0 +1,99 @@ +use proc_macro2::{Literal, Span, TokenStream}; +use quote::ToTokens; +use std::collections::HashSet; +use std::fmt::{self, Display}; +use std::str::FromStr; +use syn::{Error, Expr, Lit, Result}; + +pub struct DiscriminantSet { + values: HashSet, + previous: Option, +} + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct Discriminant { + magnitude: u32, +} + +impl DiscriminantSet { + pub fn new() -> Self { + DiscriminantSet { + values: HashSet::new(), + previous: None, + } + } + + pub fn insert(&mut self, expr: &Expr) -> Result { + let discriminant = expr_to_discriminant(expr)?; + insert(self, discriminant) + } + + pub fn insert_next(&mut self) -> Result { + let discriminant = match self.previous { + None => Discriminant::zero(), + Some(mut discriminant) => { + if discriminant.magnitude == u32::MAX { + let msg = format!("discriminant overflow on value after {}", u32::MAX); + return Err(Error::new(Span::call_site(), msg)); + } + discriminant.magnitude += 1; + discriminant + } + }; + insert(self, discriminant) + } +} + +fn expr_to_discriminant(expr: &Expr) -> Result { + if let Expr::Lit(expr) = expr { + if let Lit::Int(lit) = &expr.lit { + return lit.base10_parse::(); + } + } + Err(Error::new_spanned( + expr, + "enums with non-integer literal discriminants are not supported yet", + )) +} + +fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result { + if set.values.insert(discriminant) { + set.previous = Some(discriminant); + Ok(discriminant) + } else { + let msg = format!("discriminant value `{}` already exists", discriminant); + Err(Error::new(Span::call_site(), msg)) + } +} + +impl Discriminant { + fn zero() -> Self { + Discriminant { magnitude: 0 } + } +} + +impl Display for Discriminant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.magnitude, f) + } +} + +impl ToTokens for Discriminant { + fn to_tokens(&self, tokens: &mut TokenStream) { + Literal::u32_unsuffixed(self.magnitude).to_tokens(tokens); + } +} + +impl FromStr for Discriminant { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(magnitude) => Ok(Discriminant { magnitude }), + Err(_) => Err(Error::new( + Span::call_site(), + "discriminant value outside of supported range", + )), + } + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index d21c083..c109229 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -4,6 +4,7 @@ pub mod atom; mod attrs; pub mod check; mod derive; +mod discriminant; mod doc; pub mod error; pub mod ident; @@ -17,6 +18,7 @@ pub mod symbol; mod tokens; pub mod types; +use self::discriminant::Discriminant; use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; @@ -106,7 +108,7 @@ pub struct Receiver { pub struct Variant { pub ident: Ident, - pub discriminant: u32, + pub discriminant: Discriminant, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index f84ff32..eff14c0 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,3 +1,4 @@ +use crate::syntax::discriminant::DiscriminantSet; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -6,15 +7,12 @@ use crate::syntax::{ }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use std::collections::HashSet; -use std::u32; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ - Abi, Attribute, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, - ForeignItemType, GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, - PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, - TypeReference, TypeSlice, Variant as RustVariant, + Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -108,8 +106,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { let doc = attrs::parse_doc(cx, &item.attrs); let mut variants = Vec::new(); - let mut discriminants = HashSet::new(); - let mut prev_discriminant = None; + let mut discriminants = DiscriminantSet::new(); for variant in item.variants { match variant.fields { Fields::Unit => {} @@ -120,21 +117,19 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { )); } } - if variant.discriminant.is_none() && prev_discriminant == Some(u32::MAX) { - let msg = format!("discriminant overflow on value after {}", u32::MAX); - return Err(Error::new_spanned(variant, msg)); - } - let discriminant = - parse_discriminant(&variant)?.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - return Err(Error::new_spanned(variant, msg)); - } + let expr = variant.discriminant.as_ref().map(|(_, expr)| expr); + let try_discriminant = match &expr { + Some(lit) => discriminants.insert(lit), + None => discriminants.insert_next(), + }; + let discriminant = match try_discriminant { + Ok(discriminant) => discriminant, + Err(err) => return Err(Error::new_spanned(variant, err)), + }; variants.push(Variant { ident: variant.ident, discriminant, }); - prev_discriminant = Some(discriminant); } Ok(Api::Enum(Enum { @@ -146,28 +141,6 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { })) } -fn parse_discriminant(variant: &RustVariant) -> Result> { - match &variant.discriminant { - None => Ok(None), - Some(( - _, - Expr::Lit(ExprLit { - lit: Lit::Int(n), .. - }), - )) => match n.base10_parse() { - Ok(val) => Ok(Some(val)), - Err(_) => Err(Error::new_spanned( - variant, - "cannot parse enum discriminant as an integer", - )), - }, - _ => Err(Error::new_spanned( - variant, - "enums with non-integer literal discriminants are not supported yet", - )), - } -} - fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { let lang = match parse_lang(foreign_mod.abi) { Ok(lang) => lang, From 69c7960cb2d5e9a3daa3dfa535c8017da53b801a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 592/2232] Parse negative discriminants --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 2a55468..06a6d1b 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -3,7 +3,7 @@ use quote::ToTokens; use std::collections::HashSet; use std::fmt::{self, Display}; use std::str::FromStr; -use syn::{Error, Expr, Lit, Result}; +use syn::{Error, Expr, Lit, Result, Token, UnOp}; pub struct DiscriminantSet { values: HashSet, @@ -12,6 +12,7 @@ pub struct DiscriminantSet { #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct Discriminant { + negative: bool, magnitude: u32, } @@ -31,6 +32,13 @@ impl DiscriminantSet { pub fn insert_next(&mut self) -> Result { let discriminant = match self.previous { None => Discriminant::zero(), + Some(mut discriminant) if discriminant.negative => { + discriminant.magnitude -= 1; + if discriminant.magnitude == 0 { + discriminant.negative = false; + } + discriminant + } Some(mut discriminant) => { if discriminant.magnitude == u32::MAX { let msg = format!("discriminant overflow on value after {}", u32::MAX); @@ -45,10 +53,20 @@ impl DiscriminantSet { } fn expr_to_discriminant(expr: &Expr) -> Result { - if let Expr::Lit(expr) = expr { - if let Lit::Int(lit) = &expr.lit { - return lit.base10_parse::(); + match expr { + Expr::Lit(expr) => { + if let Lit::Int(lit) = &expr.lit { + return lit.base10_parse::(); + } + } + Expr::Unary(unary) => { + if let UnOp::Neg(_) = unary.op { + let mut discriminant = expr_to_discriminant(&unary.expr)?; + discriminant.negative ^= true; + return Ok(discriminant); + } } + _ => {} } Err(Error::new_spanned( expr, @@ -68,18 +86,27 @@ fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result Self { - Discriminant { magnitude: 0 } + Discriminant { + negative: false, + magnitude: 0, + } } } impl Display for Discriminant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.negative { + f.write_str("-")?; + } Display::fmt(&self.magnitude, f) } } impl ToTokens for Discriminant { fn to_tokens(&self, tokens: &mut TokenStream) { + if self.negative { + Token![-](Span::call_site()).to_tokens(tokens); + } Literal::u32_unsuffixed(self.magnitude).to_tokens(tokens); } } @@ -87,9 +114,16 @@ impl ToTokens for Discriminant { impl FromStr for Discriminant { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(mut s: &str) -> Result { + let negative = s.starts_with('-'); + if negative { + s = &s[1..]; + } match s.parse::() { - Ok(magnitude) => Ok(Discriminant { magnitude }), + Ok(magnitude) => Ok(Discriminant { + negative, + magnitude, + }), Err(_) => Err(Error::new( Span::call_site(), "discriminant value outside of supported range", From 2b8bf6d262ef6baacd0ededee1b28e7af8266d44 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 593/2232] Preserve the original discriminant Expr --- diff --git a/syntax/mod.rs b/syntax/mod.rs index c109229..e1402a0 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -23,7 +23,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Lifetime, LitStr, Token, Type as RustType}; +use syn::{Expr, Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::Derive; @@ -109,6 +109,7 @@ pub struct Receiver { pub struct Variant { pub ident: Ident, pub discriminant: Discriminant, + pub expr: Option, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index eff14c0..3ca63ab 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -126,9 +126,11 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { Ok(discriminant) => discriminant, Err(err) => return Err(Error::new_spanned(variant, err)), }; + let expr = variant.discriminant.map(|(_, expr)| expr); variants.push(Variant { ident: variant.ident, discriminant, + expr, }); } From 9bcb4c6d0b48553dbe56b891b49b25cc8f53d0ec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 594/2232] Extract integer suffix of discriminants as the repr --- diff --git a/syntax/atom.rs b/syntax/atom.rs index a2ff7b7..6e5fa88 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -23,8 +23,12 @@ pub enum Atom { impl Atom { pub fn from(ident: &Ident) -> Option { + Self::from_str(ident.to_string().as_str()) + } + + pub fn from_str(s: &str) -> Option { use self::Atom::*; - match ident.to_string().as_str() { + match s { "bool" => Some(Bool), "u8" => Some(U8), "u16" => Some(U16), diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 06a6d1b..7293824 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -1,3 +1,4 @@ +use crate::syntax::Atom::{self, *}; use proc_macro2::{Literal, Span, TokenStream}; use quote::ToTokens; use std::collections::HashSet; @@ -6,6 +7,7 @@ use std::str::FromStr; use syn::{Error, Expr, Lit, Result, Token, UnOp}; pub struct DiscriminantSet { + repr: Option, values: HashSet, previous: Option, } @@ -19,13 +21,15 @@ pub struct Discriminant { impl DiscriminantSet { pub fn new() -> Self { DiscriminantSet { + repr: None, values: HashSet::new(), previous: None, } } pub fn insert(&mut self, expr: &Expr) -> Result { - let discriminant = expr_to_discriminant(expr)?; + let (discriminant, repr) = expr_to_discriminant(expr)?; + self.repr = self.repr.or(repr); insert(self, discriminant) } @@ -52,18 +56,20 @@ impl DiscriminantSet { } } -fn expr_to_discriminant(expr: &Expr) -> Result { +fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option)> { match expr { Expr::Lit(expr) => { if let Lit::Int(lit) = &expr.lit { - return lit.base10_parse::(); + let discriminant = lit.base10_parse::()?; + let repr = parse_int_suffix(lit.suffix())?; + return Ok((discriminant, repr)); } } Expr::Unary(unary) => { if let UnOp::Neg(_) = unary.op { - let mut discriminant = expr_to_discriminant(&unary.expr)?; + let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?; discriminant.negative ^= true; - return Ok(discriminant); + return Ok((discriminant, repr)); } } _ => {} @@ -131,3 +137,17 @@ impl FromStr for Discriminant { } } } + +fn parse_int_suffix(suffix: &str) -> Result> { + 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)) +} From f1715fa9943c0909780fc3eb240767e2c437168d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 595/2232] Add const bound data for various discriminant reprs --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 7293824..e14c6a0 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -91,12 +91,31 @@ fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result Self { + const fn zero() -> Self { Discriminant { negative: false, magnitude: 0, } } + + const fn pos(u: u32) -> Self { + Discriminant { + negative: false, + magnitude: u, + } + } + + const fn neg(i: i32) -> Self { + Discriminant { + negative: i < 0, + // This is `i.abs() as u32` but without overflow on MIN. Uses the + // fact that MIN.wrapping_abs() wraps back to MIN whose binary + // representation is 1<<31, and thus the `as u32` conversion + // produces 1<<31 too which happens to be the correct unsigned + // magnitude. + magnitude: i.wrapping_abs() as u32, + } + } } impl Display for Discriminant { @@ -151,3 +170,42 @@ fn parse_int_suffix(suffix: &str) -> Result> { let msg = format!("unrecognized integer suffix: `{}`", suffix); Err(Error::new(Span::call_site(), msg)) } + +struct Bounds { + repr: Atom, + min: Discriminant, + max: Discriminant, +} + +const BOUNDS: [Bounds; 6] = [ + Bounds { + repr: U8, + min: Discriminant::zero(), + max: Discriminant::pos(u8::MAX as u32), + }, + Bounds { + repr: I8, + min: Discriminant::neg(i8::MIN as i32), + max: Discriminant::pos(i8::MAX as u32), + }, + Bounds { + repr: U16, + min: Discriminant::zero(), + max: Discriminant::pos(u16::MAX as u32), + }, + Bounds { + repr: I16, + min: Discriminant::neg(i16::MIN as i32), + max: Discriminant::pos(i16::MAX as u32), + }, + Bounds { + repr: U32, + min: Discriminant::zero(), + max: Discriminant::pos(u32::MAX), + }, + Bounds { + repr: I32, + min: Discriminant::neg(i32::MIN), + max: Discriminant::pos(i32::MAX as u32), + }, +]; From e2e303f7f2bbef6366c4b55431ea5ae1f7641d4e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:32 +0000 Subject: [PATCH 596/2232] Infer enum repr based on discriminant range --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index e14c6a0..388a117 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -1,18 +1,19 @@ use crate::syntax::Atom::{self, *}; use proc_macro2::{Literal, Span, TokenStream}; use quote::ToTokens; -use std::collections::HashSet; +use std::cmp::Ordering; +use std::collections::BTreeSet; use std::fmt::{self, Display}; use std::str::FromStr; use syn::{Error, Expr, Lit, Result, Token, UnOp}; pub struct DiscriminantSet { repr: Option, - values: HashSet, + values: BTreeSet, previous: Option, } -#[derive(Copy, Clone, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct Discriminant { negative: bool, magnitude: u32, @@ -22,7 +23,7 @@ impl DiscriminantSet { pub fn new() -> Self { DiscriminantSet { repr: None, - values: HashSet::new(), + values: BTreeSet::new(), previous: None, } } @@ -54,6 +55,24 @@ impl DiscriminantSet { }; insert(self, discriminant) } + + pub fn inferred_repr(&self) -> Result { + 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 bounds in &BOUNDS { + if bounds.min <= min && max <= bounds.max { + return Ok(bounds.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)> { @@ -157,6 +176,23 @@ impl FromStr for Discriminant { } } +impl Ord for Discriminant { + fn cmp(&self, other: &Self) -> Ordering { + match (self.negative, other.negative) { + (true, true) => self.magnitude.cmp(&other.magnitude).reverse(), + (true, false) => Ordering::Less, // negative < positive + (false, true) => Ordering::Greater, // positive > negative + (false, false) => self.magnitude.cmp(&other.magnitude), + } + } +} + +impl PartialOrd for Discriminant { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + fn parse_int_suffix(suffix: &str) -> Result> { if suffix.is_empty() { return Ok(None); diff --git a/syntax/mod.rs b/syntax/mod.rs index e1402a0..4702a69 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -63,6 +63,7 @@ pub struct Enum { pub ident: Ident, pub brace_token: Brace, pub variants: Vec, + pub repr: Atom, } pub struct ExternFn { diff --git a/syntax/parse.rs b/syntax/parse.rs index 3ca63ab..1525fbc 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,7 +6,7 @@ use crate::syntax::{ Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ @@ -134,12 +134,24 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { }); } + let enum_token = item.enum_token; + let brace_token = item.brace_token; + + let repr = match discriminants.inferred_repr() { + Ok(repr) => repr, + Err(err) => { + let span = quote_spanned!(brace_token.span=> #enum_token {}); + return Err(Error::new_spanned(span, err)); + } + }; + Ok(Api::Enum(Enum { doc, - enum_token: item.enum_token, + enum_token, ident: item.ident, - brace_token: item.brace_token, + brace_token, variants, + repr, })) } From 0435a818428856aa8987cbd8702fbc60a365b9a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:33 +0000 Subject: [PATCH 597/2232] Recover from some enum parsing errors --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 1525fbc..ab5dcc1 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -111,10 +111,8 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { match variant.fields { Fields::Unit => {} _ => { - return Err(Error::new_spanned( - variant, - "enums with data are not supported yet", - )); + cx.error(variant, "enums with data are not supported yet"); + break; } } let expr = variant.discriminant.as_ref().map(|(_, expr)| expr); @@ -124,7 +122,10 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { }; let discriminant = match try_discriminant { Ok(discriminant) => discriminant, - Err(err) => return Err(Error::new_spanned(variant, err)), + Err(err) => { + cx.error(variant, err); + break; + } }; let expr = variant.discriminant.map(|(_, expr)| expr); variants.push(Variant { @@ -137,13 +138,15 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { let enum_token = item.enum_token; let brace_token = item.brace_token; - let repr = match discriminants.inferred_repr() { - Ok(repr) => repr, + let mut repr = U8; + match discriminants.inferred_repr() { + Ok(inferred) => repr = inferred, Err(err) => { let span = quote_spanned!(brace_token.span=> #enum_token {}); - return Err(Error::new_spanned(span, err)); + cx.error(span, err); + variants.clear(); } - }; + } Ok(Api::Enum(Enum { doc, From ddf69e291bc821ebd252d106e223abd9fe069415 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:57:33 +0000 Subject: [PATCH 598/2232] Parse repr attribute on enums --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index f48a210..16541a3 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,5 +1,7 @@ use crate::syntax::report::Errors; +use crate::syntax::Atom::{self, *}; use crate::syntax::{Derive, Doc}; +use proc_macro2::Ident; use syn::parse::{ParseStream, Parser as _}; use syn::{Attribute, Error, LitStr, Path, Result, Token}; @@ -7,6 +9,7 @@ use syn::{Attribute, Error, LitStr, Path, Result, Token}; pub struct Parser<'a> { pub doc: Option<&'a mut Doc>, pub derives: Option<&'a mut Vec>, + pub repr: Option<&'a mut Option>, } pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { @@ -44,6 +47,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } 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) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -69,3 +82,18 @@ fn parse_derive_attribute(input: ParseStream) -> Result> { }) .collect() } + +fn parse_repr_attribute(input: ParseStream) -> Result { + 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 => return Ok(atom), + _ => {} + } + } + Err(Error::new_spanned( + begin.token_stream(), + "unrecognized repr", + )) +} diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 388a117..c65b3f4 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -20,9 +20,9 @@ pub struct Discriminant { } impl DiscriminantSet { - pub fn new() -> Self { + pub fn new(repr: Option) -> Self { DiscriminantSet { - repr: None, + repr, values: BTreeSet::new(), previous: None, } diff --git a/syntax/parse.rs b/syntax/parse.rs index ab5dcc1..a11a5c8 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -60,6 +60,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), + ..Default::default() }, ); @@ -103,10 +104,20 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { )); } - let doc = attrs::parse_doc(cx, &item.attrs); + let mut doc = Doc::new(); + let mut repr = None; + attrs::parse( + cx, + &item.attrs, + attrs::Parser { + doc: Some(&mut doc), + repr: Some(&mut repr), + ..Default::default() + }, + ); let mut variants = Vec::new(); - let mut discriminants = DiscriminantSet::new(); + let mut discriminants = DiscriminantSet::new(repr); for variant in item.variants { match variant.fields { Fields::Unit => {} From 5966f7b3f6a43a19f62b0b26759ec3bf75bb60dd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 05:59:56 +0000 Subject: [PATCH 599/2232] Detect out of bounds when inserting discriminant --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index c65b3f4..4caa42b 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -100,6 +100,21 @@ fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option)> { } fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result { + if let Some(expected_repr) = set.repr { + for bounds in &BOUNDS { + if bounds.repr != expected_repr { + continue; + } + if bounds.min <= discriminant && discriminant <= bounds.max { + break; + } + let msg = format!( + "discriminant value `{}` is outside the limits of {}", + discriminant, expected_repr, + ); + return Err(Error::new(Span::call_site(), msg)); + } + } if set.values.insert(discriminant) { set.previous = Some(discriminant); Ok(discriminant) From 94cce00fddbc7dd5617872730ae8eb1d2fbdfaf6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:19:39 +0000 Subject: [PATCH 600/2232] Detect mismatched suffix on discriminant values --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 4caa42b..d683eef 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -30,7 +30,14 @@ impl DiscriminantSet { pub fn insert(&mut self, expr: &Expr) -> Result { let (discriminant, repr) = expr_to_discriminant(expr)?; - self.repr = self.repr.or(repr); + match (self.repr, repr) { + (None, _) => self.repr = 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) } From f2d584101dd02845e4fa349b6c21e512e782478e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:23:37 +0000 Subject: [PATCH 601/2232] Expand maximum recognized discriminant to 64 bits --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index d683eef..7ce18ff 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -16,7 +16,7 @@ pub struct DiscriminantSet { #[derive(Copy, Clone, Eq, PartialEq)] pub struct Discriminant { negative: bool, - magnitude: u32, + magnitude: u64, } impl DiscriminantSet { @@ -52,8 +52,8 @@ impl DiscriminantSet { discriminant } Some(mut discriminant) => { - if discriminant.magnitude == u32::MAX { - let msg = format!("discriminant overflow on value after {}", u32::MAX); + 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; @@ -139,22 +139,22 @@ impl Discriminant { } } - const fn pos(u: u32) -> Self { + const fn pos(u: u64) -> Self { Discriminant { negative: false, magnitude: u, } } - const fn neg(i: i32) -> Self { + const fn neg(i: i64) -> Self { Discriminant { negative: i < 0, - // This is `i.abs() as u32` but without overflow on MIN. Uses the + // 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<<31, and thus the `as u32` conversion - // produces 1<<31 too which happens to be the correct unsigned + // 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 u32, + magnitude: i.wrapping_abs() as u64, } } } @@ -173,7 +173,7 @@ impl ToTokens for Discriminant { if self.negative { Token![-](Span::call_site()).to_tokens(tokens); } - Literal::u32_unsuffixed(self.magnitude).to_tokens(tokens); + Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens); } } @@ -185,7 +185,7 @@ impl FromStr for Discriminant { if negative { s = &s[1..]; } - match s.parse::() { + match s.parse::() { Ok(magnitude) => Ok(Discriminant { negative, magnitude, @@ -235,35 +235,45 @@ struct Bounds { max: Discriminant, } -const BOUNDS: [Bounds; 6] = [ +const BOUNDS: [Bounds; 8] = [ Bounds { repr: U8, min: Discriminant::zero(), - max: Discriminant::pos(u8::MAX as u32), + max: Discriminant::pos(u8::MAX as u64), }, Bounds { repr: I8, - min: Discriminant::neg(i8::MIN as i32), - max: Discriminant::pos(i8::MAX as u32), + min: Discriminant::neg(i8::MIN as i64), + max: Discriminant::pos(i8::MAX as u64), }, Bounds { repr: U16, min: Discriminant::zero(), - max: Discriminant::pos(u16::MAX as u32), + max: Discriminant::pos(u16::MAX as u64), }, Bounds { repr: I16, - min: Discriminant::neg(i16::MIN as i32), - max: Discriminant::pos(i16::MAX as u32), + min: Discriminant::neg(i16::MIN as i64), + max: Discriminant::pos(i16::MAX as u64), }, Bounds { repr: U32, min: Discriminant::zero(), - max: Discriminant::pos(u32::MAX), + max: Discriminant::pos(u32::MAX as u64), }, Bounds { repr: I32, - min: Discriminant::neg(i32::MIN), - max: Discriminant::pos(i32::MAX as u32), + min: Discriminant::neg(i32::MIN as i64), + max: Discriminant::pos(i32::MAX as u64), + }, + Bounds { + repr: U64, + min: Discriminant::zero(), + max: Discriminant::pos(u64::MAX), + }, + Bounds { + repr: I64, + min: Discriminant::neg(i64::MIN), + max: Discriminant::pos(i64::MAX as u64), }, ]; diff --git a/tests/ui/enum_overflows.rs b/tests/ui/enum_overflows.rs index 3f351f0..29de1a0 100644 --- a/tests/ui/enum_overflows.rs +++ b/tests/ui/enum_overflows.rs @@ -1,14 +1,14 @@ #[cxx::bridge] mod ffi { enum Good1 { - A = 0xffffffff, + A = 0xFFFF_FFFF_FFFF_FFFF, } enum Good2 { - B = 0xffffffff, + B = 0xFFFF_FFFF_FFFF_FFFF, C = 2020, } enum Bad { - D = 0xfffffffe, + D = 0xFFFF_FFFF_FFFF_FFFE, E, F, } diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr index db92f7f..ed58b61 100644 --- a/tests/ui/enum_overflows.stderr +++ b/tests/ui/enum_overflows.stderr @@ -1,4 +1,4 @@ -error: discriminant overflow on value after 4294967295 +error: discriminant overflow on value after 18446744073709551615 --> $DIR/enum_overflows.rs:13:9 | 13 | F, From f85431239d8b81ab34f4d1da0056324df22dbfea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:28:04 +0000 Subject: [PATCH 602/2232] Test discriminant outside of the repr's bounds --- diff --git a/tests/ui/enum_out_of_bounds.rs b/tests/ui/enum_out_of_bounds.rs new file mode 100644 index 0000000..1aec5f0 --- /dev/null +++ b/tests/ui/enum_out_of_bounds.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + #[repr(u32)] + enum Bad { + A = 0xFFFF_FFFF_FFFF_FFFF, + } +} + +fn main() {} diff --git a/tests/ui/enum_out_of_bounds.stderr b/tests/ui/enum_out_of_bounds.stderr new file mode 100644 index 0000000..f437ea1 --- /dev/null +++ b/tests/ui/enum_out_of_bounds.stderr @@ -0,0 +1,5 @@ +error: discriminant value `18446744073709551615` is outside the limits of u32 + --> $DIR/enum_out_of_bounds.rs:5:9 + | +5 | A = 0xFFFF_FFFF_FFFF_FFFF, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ From b24f52e2892997dd07aff505625ed146c6e746cf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:29:12 +0000 Subject: [PATCH 603/2232] Test inconsistent suffix on enum discriminants --- diff --git a/tests/ui/enum_inconsistent.rs b/tests/ui/enum_inconsistent.rs new file mode 100644 index 0000000..cd5ffa5 --- /dev/null +++ b/tests/ui/enum_inconsistent.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + enum Bad { + A = 1u16, + B = 2i64, + } +} + +fn main() {} diff --git a/tests/ui/enum_inconsistent.stderr b/tests/ui/enum_inconsistent.stderr new file mode 100644 index 0000000..c5b427a --- /dev/null +++ b/tests/ui/enum_inconsistent.stderr @@ -0,0 +1,5 @@ +error: expected u16, found i64 + --> $DIR/enum_inconsistent.rs:5:9 + | +5 | B = 2i64, + | ^^^^^^^^ From 560661abe557f21c087223325ab0488d806c0717 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:32:31 +0000 Subject: [PATCH 604/2232] Test unsatisfiable discriminant range --- diff --git a/tests/ui/enum_unsatisfiable.rs b/tests/ui/enum_unsatisfiable.rs new file mode 100644 index 0000000..6191287 --- /dev/null +++ b/tests/ui/enum_unsatisfiable.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + enum Bad { + A = -0xFFFF_FFFF_FFFF_FFFF, + B = 0xFFFF_FFFF_FFFF_FFFF, + } +} + +fn main() {} diff --git a/tests/ui/enum_unsatisfiable.stderr b/tests/ui/enum_unsatisfiable.stderr new file mode 100644 index 0000000..99852ca --- /dev/null +++ b/tests/ui/enum_unsatisfiable.stderr @@ -0,0 +1,8 @@ +error: these discriminant values do not fit in any supported enum repr type + --> $DIR/enum_unsatisfiable.rs:3:5 + | +3 | / enum Bad { +4 | | A = -0xFFFF_FFFF_FFFF_FFFF, +5 | | B = 0xFFFF_FFFF_FFFF_FFFF, +6 | | } + | |_____^ From c605e6fa610c3ed08799aef11d08a3d4361433c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:37:12 +0000 Subject: [PATCH 605/2232] Respect inferred enum repr in Rust code generator --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fc129c6..f28d955 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -143,6 +143,7 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; + let repr = enm.repr; let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -155,7 +156,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub struct #ident { - pub repr: u32, + pub repr: #repr, } #[allow(non_upper_case_globals)] diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 9a1fee0..4ed264a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, + Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; @@ -75,6 +75,12 @@ impl ToTokens for Derive { } } +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 ExternType { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 93a8957..a5a0215 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A { repr: 2u32..=std::u32::MAX }` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=std::u8::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A { repr: 2u32..=std::u32::MAX }` not covered + | ^ pattern `A { repr: 2u8..=std::u8::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` From f6a89f2813dda6b21db9726dce07338b2ccddfbf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:45:51 +0000 Subject: [PATCH 606/2232] Respect inferred enum repr in C++ code generator --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8f54bd7..0849d90 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -365,7 +365,9 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "enum class {} : uint32_t {{", enm.ident); + write!(out, "enum class {} : ", enm.ident); + write_atom(out, enm.repr); + writeln!(out, " {{"); for variant in &enm.variants { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } @@ -373,16 +375,15 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - writeln!( - out, - "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", - enm.ident - ); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write_atom(out, enm.repr); + writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { + write!(out, "static_assert(static_cast<"); + write_atom(out, enm.repr); writeln!( out, - "static_assert(static_cast({}::{}) == {}, - \"disagrees with the value in #[cxx::bridge]\");", + ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", enm.ident, variant.ident, variant.discriminant, ); } @@ -849,21 +850,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), + Some(atom) => write_atom(out, atom), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { @@ -922,6 +909,26 @@ fn write_type(out: &mut OutFile, ty: &Type) { } } +fn write_atom(out: &mut OutFile, atom: Atom) { + match atom { + Bool => write!(out, "bool"), + U8 => write!(out, "uint8_t"), + U16 => write!(out, "uint16_t"), + U32 => write!(out, "uint32_t"), + U64 => write!(out, "uint64_t"), + Usize => write!(out, "size_t"), + I8 => write!(out, "int8_t"), + I16 => write!(out, "int16_t"), + I32 => write!(out, "int32_t"), + I64 => write!(out, "int64_t"), + Isize => write!(out, "::rust::isize"), + F32 => write!(out, "float"), + F64 => write!(out, "double"), + CxxString => write!(out, "::std::string"), + RustString => write!(out, "::rust::String"), + } +} + fn write_type_space(out: &mut OutFile, ty: &Type) { write_type(out, ty); write_space_after_type(out, ty); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9457229..d4e96fc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -44,7 +44,7 @@ pub mod ffi { fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; - fn c_return_enum(n: u32) -> Enum; + fn c_return_enum(n: u16) -> Enum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -89,6 +89,7 @@ pub mod ffi { type COwnedEnum; } + #[repr(u32)] enum COwnedEnum { CVal1, CVal2, diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 21637d8..af7a4a0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -107,10 +107,10 @@ size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } -Enum c_return_enum(uint32_t n) { - if (n <= static_cast(Enum::AVal)) { +Enum c_return_enum(uint16_t n) { + if (n <= static_cast(Enum::AVal)) { return Enum::AVal; - } else if (n <= static_cast(Enum::BVal)) { + } else if (n <= static_cast(Enum::BVal)) { return Enum::BVal; } else { return Enum::CVal; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 795b4a9..1173a0a 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -7,7 +7,7 @@ namespace tests { struct R; struct Shared; -enum class Enum : uint32_t; +enum class Enum : uint16_t; class C { public: @@ -46,7 +46,7 @@ rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); -Enum c_return_enum(uint32_t n); +Enum c_return_enum(uint16_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); From 17451dea3423eccb6462a71f500d40a7d15d34c1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 06:49:12 +0000 Subject: [PATCH 607/2232] Restore rust 1.42 compatibility --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 7ce18ff..67fa14d 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -5,6 +5,7 @@ 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 { @@ -239,41 +240,41 @@ const BOUNDS: [Bounds; 8] = [ Bounds { repr: U8, min: Discriminant::zero(), - max: Discriminant::pos(u8::MAX as u64), + max: Discriminant::pos(std::u8::MAX as u64), }, Bounds { repr: I8, - min: Discriminant::neg(i8::MIN as i64), - max: Discriminant::pos(i8::MAX as u64), + min: Discriminant::neg(std::i8::MIN as i64), + max: Discriminant::pos(std::i8::MAX as u64), }, Bounds { repr: U16, min: Discriminant::zero(), - max: Discriminant::pos(u16::MAX as u64), + max: Discriminant::pos(std::u16::MAX as u64), }, Bounds { repr: I16, - min: Discriminant::neg(i16::MIN as i64), - max: Discriminant::pos(i16::MAX as u64), + min: Discriminant::neg(std::i16::MIN as i64), + max: Discriminant::pos(std::i16::MAX as u64), }, Bounds { repr: U32, min: Discriminant::zero(), - max: Discriminant::pos(u32::MAX as u64), + max: Discriminant::pos(std::u32::MAX as u64), }, Bounds { repr: I32, - min: Discriminant::neg(i32::MIN as i64), - max: Discriminant::pos(i32::MAX as u64), + min: Discriminant::neg(std::i32::MIN as i64), + max: Discriminant::pos(std::i32::MAX as u64), }, Bounds { repr: U64, min: Discriminant::zero(), - max: Discriminant::pos(u64::MAX), + max: Discriminant::pos(std::u64::MAX), }, Bounds { repr: I64, - min: Discriminant::neg(i64::MIN), - max: Discriminant::pos(i64::MAX as u64), + min: Discriminant::neg(std::i64::MIN), + max: Discriminant::pos(std::i64::MAX as u64), }, ]; From a954fcaddeb7a308af33b1fa9b39939453eea4d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:14:44 +0000 Subject: [PATCH 608/2232] Merge pull request #196 from dtolnay/enum Choose enum discriminant type based on repr attributes, suffixes, values --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8f54bd7..0849d90 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -365,7 +365,9 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "enum class {} : uint32_t {{", enm.ident); + write!(out, "enum class {} : ", enm.ident); + write_atom(out, enm.repr); + writeln!(out, " {{"); for variant in &enm.variants { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } @@ -373,16 +375,15 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - writeln!( - out, - "static_assert(sizeof({}) == sizeof(uint32_t), \"incorrect size\");", - enm.ident - ); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write_atom(out, enm.repr); + writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { + write!(out, "static_assert(static_cast<"); + write_atom(out, enm.repr); writeln!( out, - "static_assert(static_cast({}::{}) == {}, - \"disagrees with the value in #[cxx::bridge]\");", + ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", enm.ident, variant.ident, variant.discriminant, ); } @@ -849,21 +850,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(ident) { - Some(Bool) => write!(out, "bool"), - Some(U8) => write!(out, "uint8_t"), - Some(U16) => write!(out, "uint16_t"), - Some(U32) => write!(out, "uint32_t"), - Some(U64) => write!(out, "uint64_t"), - Some(Usize) => write!(out, "size_t"), - Some(I8) => write!(out, "int8_t"), - Some(I16) => write!(out, "int16_t"), - Some(I32) => write!(out, "int32_t"), - Some(I64) => write!(out, "int64_t"), - Some(Isize) => write!(out, "::rust::isize"), - Some(F32) => write!(out, "float"), - Some(F64) => write!(out, "double"), - Some(CxxString) => write!(out, "::std::string"), - Some(RustString) => write!(out, "::rust::String"), + Some(atom) => write_atom(out, atom), None => write!(out, "{}", ident), }, Type::RustBox(ty) => { @@ -922,6 +909,26 @@ fn write_type(out: &mut OutFile, ty: &Type) { } } +fn write_atom(out: &mut OutFile, atom: Atom) { + match atom { + Bool => write!(out, "bool"), + U8 => write!(out, "uint8_t"), + U16 => write!(out, "uint16_t"), + U32 => write!(out, "uint32_t"), + U64 => write!(out, "uint64_t"), + Usize => write!(out, "size_t"), + I8 => write!(out, "int8_t"), + I16 => write!(out, "int16_t"), + I32 => write!(out, "int32_t"), + I64 => write!(out, "int64_t"), + Isize => write!(out, "::rust::isize"), + F32 => write!(out, "float"), + F64 => write!(out, "double"), + CxxString => write!(out, "::std::string"), + RustString => write!(out, "::rust::String"), + } +} + fn write_type_space(out: &mut OutFile, ty: &Type) { write_type(out, ty); write_space_after_type(out, ty); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fc129c6..f28d955 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -143,6 +143,7 @@ fn expand_struct(strct: &Struct) -> TokenStream { fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; + let repr = enm.repr; let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -155,7 +156,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub struct #ident { - pub repr: u32, + pub repr: #repr, } #[allow(non_upper_case_globals)] diff --git a/syntax/atom.rs b/syntax/atom.rs index a2ff7b7..6e5fa88 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -23,8 +23,12 @@ pub enum Atom { impl Atom { pub fn from(ident: &Ident) -> Option { + Self::from_str(ident.to_string().as_str()) + } + + pub fn from_str(s: &str) -> Option { use self::Atom::*; - match ident.to_string().as_str() { + match s { "bool" => Some(Bool), "u8" => Some(U8), "u16" => Some(U16), diff --git a/syntax/attrs.rs b/syntax/attrs.rs index f48a210..16541a3 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,5 +1,7 @@ use crate::syntax::report::Errors; +use crate::syntax::Atom::{self, *}; use crate::syntax::{Derive, Doc}; +use proc_macro2::Ident; use syn::parse::{ParseStream, Parser as _}; use syn::{Attribute, Error, LitStr, Path, Result, Token}; @@ -7,6 +9,7 @@ use syn::{Attribute, Error, LitStr, Path, Result, Token}; pub struct Parser<'a> { pub doc: Option<&'a mut Doc>, pub derives: Option<&'a mut Vec>, + pub repr: Option<&'a mut Option>, } pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { @@ -44,6 +47,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } 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) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -69,3 +82,18 @@ fn parse_derive_attribute(input: ParseStream) -> Result> { }) .collect() } + +fn parse_repr_attribute(input: ParseStream) -> Result { + 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 => return Ok(atom), + _ => {} + } + } + Err(Error::new_spanned( + begin.token_stream(), + "unrecognized repr", + )) +} diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs new file mode 100644 index 0000000..67fa14d --- /dev/null +++ b/syntax/discriminant.rs @@ -0,0 +1,280 @@ +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, + values: BTreeSet, + previous: Option, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Discriminant { + negative: bool, + magnitude: u64, +} + +impl DiscriminantSet { + pub fn new(repr: Option) -> Self { + DiscriminantSet { + repr, + values: BTreeSet::new(), + previous: None, + } + } + + pub fn insert(&mut self, expr: &Expr) -> Result { + let (discriminant, repr) = expr_to_discriminant(expr)?; + match (self.repr, repr) { + (None, _) => self.repr = 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 { + let discriminant = match self.previous { + None => Discriminant::zero(), + Some(mut discriminant) if discriminant.negative => { + discriminant.magnitude -= 1; + if discriminant.magnitude == 0 { + discriminant.negative = false; + } + discriminant + } + Some(mut discriminant) => { + 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 { + 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 bounds in &BOUNDS { + if bounds.min <= min && max <= bounds.max { + return Ok(bounds.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)> { + match expr { + Expr::Lit(expr) => { + if let Lit::Int(lit) = &expr.lit { + let discriminant = lit.base10_parse::()?; + 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.negative ^= true; + 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 { + if let Some(expected_repr) = set.repr { + for bounds in &BOUNDS { + if bounds.repr != expected_repr { + continue; + } + if bounds.min <= discriminant && discriminant <= bounds.max { + break; + } + let msg = format!( + "discriminant value `{}` is outside the limits of {}", + discriminant, expected_repr, + ); + return Err(Error::new(Span::call_site(), msg)); + } + } + if set.values.insert(discriminant) { + set.previous = Some(discriminant); + Ok(discriminant) + } else { + let msg = format!("discriminant value `{}` already exists", discriminant); + Err(Error::new(Span::call_site(), msg)) + } +} + +impl Discriminant { + const fn zero() -> Self { + Discriminant { + negative: false, + magnitude: 0, + } + } + + const fn pos(u: u64) -> Self { + Discriminant { + negative: false, + magnitude: u, + } + } + + const fn neg(i: i64) -> Self { + Discriminant { + negative: i < 0, + // 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, + } + } +} + +impl Display for Discriminant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.negative { + f.write_str("-")?; + } + Display::fmt(&self.magnitude, f) + } +} + +impl ToTokens for Discriminant { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.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 { + let negative = s.starts_with('-'); + if negative { + s = &s[1..]; + } + match s.parse::() { + Ok(magnitude) => Ok(Discriminant { + negative, + magnitude, + }), + Err(_) => Err(Error::new( + Span::call_site(), + "discriminant value outside of supported range", + )), + } + } +} + +impl Ord for Discriminant { + fn cmp(&self, other: &Self) -> Ordering { + match (self.negative, other.negative) { + (true, true) => self.magnitude.cmp(&other.magnitude).reverse(), + (true, false) => Ordering::Less, // negative < positive + (false, true) => Ordering::Greater, // positive > negative + (false, false) => self.magnitude.cmp(&other.magnitude), + } + } +} + +impl PartialOrd for Discriminant { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn parse_int_suffix(suffix: &str) -> Result> { + 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)) +} + +struct Bounds { + repr: Atom, + min: Discriminant, + max: Discriminant, +} + +const BOUNDS: [Bounds; 8] = [ + Bounds { + repr: U8, + min: Discriminant::zero(), + max: Discriminant::pos(std::u8::MAX as u64), + }, + Bounds { + repr: I8, + min: Discriminant::neg(std::i8::MIN as i64), + max: Discriminant::pos(std::i8::MAX as u64), + }, + Bounds { + repr: U16, + min: Discriminant::zero(), + max: Discriminant::pos(std::u16::MAX as u64), + }, + Bounds { + repr: I16, + min: Discriminant::neg(std::i16::MIN as i64), + max: Discriminant::pos(std::i16::MAX as u64), + }, + Bounds { + repr: U32, + min: Discriminant::zero(), + max: Discriminant::pos(std::u32::MAX as u64), + }, + Bounds { + repr: I32, + min: Discriminant::neg(std::i32::MIN as i64), + max: Discriminant::pos(std::i32::MAX as u64), + }, + Bounds { + repr: U64, + min: Discriminant::zero(), + max: Discriminant::pos(std::u64::MAX), + }, + Bounds { + repr: I64, + min: Discriminant::neg(std::i64::MIN), + max: Discriminant::pos(std::i64::MAX as u64), + }, +]; diff --git a/syntax/mod.rs b/syntax/mod.rs index d21c083..4702a69 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -4,6 +4,7 @@ pub mod atom; mod attrs; pub mod check; mod derive; +mod discriminant; mod doc; pub mod error; pub mod ident; @@ -17,11 +18,12 @@ pub mod symbol; mod tokens; pub mod types; +use self::discriminant::Discriminant; use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Lifetime, LitStr, Token, Type as RustType}; +use syn::{Expr, Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::Derive; @@ -61,6 +63,7 @@ pub struct Enum { pub ident: Ident, pub brace_token: Brace, pub variants: Vec, + pub repr: Atom, } pub struct ExternFn { @@ -106,7 +109,8 @@ pub struct Receiver { pub struct Variant { pub ident: Ident, - pub discriminant: u32, + pub discriminant: Discriminant, + pub expr: Option, } pub enum Type { diff --git a/syntax/parse.rs b/syntax/parse.rs index f84ff32..a11a5c8 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,3 +1,4 @@ +use crate::syntax::discriminant::DiscriminantSet; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -5,16 +6,13 @@ use crate::syntax::{ Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use std::collections::HashSet; -use std::u32; +use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ - Abi, Attribute, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, - ForeignItemType, GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, - PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, - TypeReference, TypeSlice, Variant as RustVariant, + Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -62,6 +60,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), + ..Default::default() }, ); @@ -105,69 +104,71 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { )); } - let doc = attrs::parse_doc(cx, &item.attrs); + let mut doc = Doc::new(); + let mut repr = None; + attrs::parse( + cx, + &item.attrs, + attrs::Parser { + doc: Some(&mut doc), + repr: Some(&mut repr), + ..Default::default() + }, + ); let mut variants = Vec::new(); - let mut discriminants = HashSet::new(); - let mut prev_discriminant = None; + let mut discriminants = DiscriminantSet::new(repr); for variant in item.variants { match variant.fields { Fields::Unit => {} _ => { - return Err(Error::new_spanned( - variant, - "enums with data are not supported yet", - )); + cx.error(variant, "enums with data are not supported yet"); + break; } } - if variant.discriminant.is_none() && prev_discriminant == Some(u32::MAX) { - let msg = format!("discriminant overflow on value after {}", u32::MAX); - return Err(Error::new_spanned(variant, msg)); - } - let discriminant = - parse_discriminant(&variant)?.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1)); - if !discriminants.insert(discriminant) { - let msg = format!("discriminant value `{}` already exists", discriminant); - return Err(Error::new_spanned(variant, msg)); - } + let expr = variant.discriminant.as_ref().map(|(_, expr)| expr); + let try_discriminant = match &expr { + Some(lit) => discriminants.insert(lit), + None => discriminants.insert_next(), + }; + let discriminant = match try_discriminant { + Ok(discriminant) => discriminant, + Err(err) => { + cx.error(variant, err); + break; + } + }; + let expr = variant.discriminant.map(|(_, expr)| expr); variants.push(Variant { ident: variant.ident, discriminant, + expr, }); - prev_discriminant = Some(discriminant); + } + + let enum_token = item.enum_token; + let brace_token = item.brace_token; + + let mut repr = U8; + match discriminants.inferred_repr() { + Ok(inferred) => repr = inferred, + Err(err) => { + let span = quote_spanned!(brace_token.span=> #enum_token {}); + cx.error(span, err); + variants.clear(); + } } Ok(Api::Enum(Enum { doc, - enum_token: item.enum_token, + enum_token, ident: item.ident, - brace_token: item.brace_token, + brace_token, variants, + repr, })) } -fn parse_discriminant(variant: &RustVariant) -> Result> { - match &variant.discriminant { - None => Ok(None), - Some(( - _, - Expr::Lit(ExprLit { - lit: Lit::Int(n), .. - }), - )) => match n.base10_parse() { - Ok(val) => Ok(Some(val)), - Err(_) => Err(Error::new_spanned( - variant, - "cannot parse enum discriminant as an integer", - )), - }, - _ => Err(Error::new_spanned( - variant, - "enums with non-integer literal discriminants are not supported yet", - )), - } -} - fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { let lang = match parse_lang(foreign_mod.abi) { Ok(lang) => lang, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 9a1fee0..4ed264a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, + Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; @@ -75,6 +75,12 @@ impl ToTokens for Derive { } } +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 ExternType { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9457229..d4e96fc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -44,7 +44,7 @@ pub mod ffi { fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; - fn c_return_enum(n: u32) -> Enum; + fn c_return_enum(n: u16) -> Enum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -89,6 +89,7 @@ pub mod ffi { type COwnedEnum; } + #[repr(u32)] enum COwnedEnum { CVal1, CVal2, diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 21637d8..af7a4a0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -107,10 +107,10 @@ size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } -Enum c_return_enum(uint32_t n) { - if (n <= static_cast(Enum::AVal)) { +Enum c_return_enum(uint16_t n) { + if (n <= static_cast(Enum::AVal)) { return Enum::AVal; - } else if (n <= static_cast(Enum::BVal)) { + } else if (n <= static_cast(Enum::BVal)) { return Enum::BVal; } else { return Enum::CVal; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 795b4a9..1173a0a 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -7,7 +7,7 @@ namespace tests { struct R; struct Shared; -enum class Enum : uint32_t; +enum class Enum : uint16_t; class C { public: @@ -46,7 +46,7 @@ rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); -Enum c_return_enum(uint32_t n); +Enum c_return_enum(uint16_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); diff --git a/tests/ui/enum_inconsistent.rs b/tests/ui/enum_inconsistent.rs new file mode 100644 index 0000000..cd5ffa5 --- /dev/null +++ b/tests/ui/enum_inconsistent.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + enum Bad { + A = 1u16, + B = 2i64, + } +} + +fn main() {} diff --git a/tests/ui/enum_inconsistent.stderr b/tests/ui/enum_inconsistent.stderr new file mode 100644 index 0000000..c5b427a --- /dev/null +++ b/tests/ui/enum_inconsistent.stderr @@ -0,0 +1,5 @@ +error: expected u16, found i64 + --> $DIR/enum_inconsistent.rs:5:9 + | +5 | B = 2i64, + | ^^^^^^^^ diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 93a8957..a5a0215 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A { repr: 2u32..=std::u32::MAX }` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=std::u8::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A { repr: 2u32..=std::u32::MAX }` not covered + | ^ pattern `A { repr: 2u8..=std::u8::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` diff --git a/tests/ui/enum_out_of_bounds.rs b/tests/ui/enum_out_of_bounds.rs new file mode 100644 index 0000000..1aec5f0 --- /dev/null +++ b/tests/ui/enum_out_of_bounds.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + #[repr(u32)] + enum Bad { + A = 0xFFFF_FFFF_FFFF_FFFF, + } +} + +fn main() {} diff --git a/tests/ui/enum_out_of_bounds.stderr b/tests/ui/enum_out_of_bounds.stderr new file mode 100644 index 0000000..f437ea1 --- /dev/null +++ b/tests/ui/enum_out_of_bounds.stderr @@ -0,0 +1,5 @@ +error: discriminant value `18446744073709551615` is outside the limits of u32 + --> $DIR/enum_out_of_bounds.rs:5:9 + | +5 | A = 0xFFFF_FFFF_FFFF_FFFF, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/enum_overflows.rs b/tests/ui/enum_overflows.rs index 3f351f0..29de1a0 100644 --- a/tests/ui/enum_overflows.rs +++ b/tests/ui/enum_overflows.rs @@ -1,14 +1,14 @@ #[cxx::bridge] mod ffi { enum Good1 { - A = 0xffffffff, + A = 0xFFFF_FFFF_FFFF_FFFF, } enum Good2 { - B = 0xffffffff, + B = 0xFFFF_FFFF_FFFF_FFFF, C = 2020, } enum Bad { - D = 0xfffffffe, + D = 0xFFFF_FFFF_FFFF_FFFE, E, F, } diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr index db92f7f..ed58b61 100644 --- a/tests/ui/enum_overflows.stderr +++ b/tests/ui/enum_overflows.stderr @@ -1,4 +1,4 @@ -error: discriminant overflow on value after 4294967295 +error: discriminant overflow on value after 18446744073709551615 --> $DIR/enum_overflows.rs:13:9 | 13 | F, diff --git a/tests/ui/enum_unsatisfiable.rs b/tests/ui/enum_unsatisfiable.rs new file mode 100644 index 0000000..6191287 --- /dev/null +++ b/tests/ui/enum_unsatisfiable.rs @@ -0,0 +1,9 @@ +#[cxx::bridge] +mod ffi { + enum Bad { + A = -0xFFFF_FFFF_FFFF_FFFF, + B = 0xFFFF_FFFF_FFFF_FFFF, + } +} + +fn main() {} diff --git a/tests/ui/enum_unsatisfiable.stderr b/tests/ui/enum_unsatisfiable.stderr new file mode 100644 index 0000000..99852ca --- /dev/null +++ b/tests/ui/enum_unsatisfiable.stderr @@ -0,0 +1,8 @@ +error: these discriminant values do not fit in any supported enum repr type + --> $DIR/enum_unsatisfiable.rs:3:5 + | +3 | / enum Bad { +4 | | A = -0xFFFF_FFFF_FFFF_FFFF, +5 | | B = 0xFFFF_FFFF_FFFF_FFFF, +6 | | } + | |_____^ From 8155e58ba1c8c26a2cb37ad054272359a507a928 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:15:12 +0000 Subject: [PATCH 609/2232] Treat unexpected tokens in repr attribute as unexpected repr --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 16541a3..4c76641 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -88,7 +88,9 @@ fn parse_repr_attribute(input: ParseStream) -> Result { let ident: Ident = input.parse()?; if let Some(atom) = Atom::from(&ident) { match atom { - U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize => return Ok(atom), + U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => { + return Ok(atom); + } _ => {} } } From 9f7c55acdee93a651fab5a9c74923615664d8519 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:19:02 +0000 Subject: [PATCH 610/2232] Rename discriminant Bounds to Limits This is aligned with "limits" as used in std::numeric_limits in C++. --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 67fa14d..35ef4cb 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -73,9 +73,9 @@ impl DiscriminantSet { } let min = *self.values.iter().next().unwrap(); let max = *self.values.iter().next_back().unwrap(); - for bounds in &BOUNDS { - if bounds.min <= min && max <= bounds.max { - return Ok(bounds.repr); + 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"; @@ -109,11 +109,11 @@ fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option)> { fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result { if let Some(expected_repr) = set.repr { - for bounds in &BOUNDS { - if bounds.repr != expected_repr { + for limits in &LIMITS { + if limits.repr != expected_repr { continue; } - if bounds.min <= discriminant && discriminant <= bounds.max { + if limits.min <= discriminant && discriminant <= limits.max { break; } let msg = format!( @@ -230,49 +230,49 @@ fn parse_int_suffix(suffix: &str) -> Result> { Err(Error::new(Span::call_site(), msg)) } -struct Bounds { +struct Limits { repr: Atom, min: Discriminant, max: Discriminant, } -const BOUNDS: [Bounds; 8] = [ - Bounds { +const LIMITS: [Limits; 8] = [ + Limits { repr: U8, min: Discriminant::zero(), max: Discriminant::pos(std::u8::MAX as u64), }, - Bounds { + Limits { repr: I8, min: Discriminant::neg(std::i8::MIN as i64), max: Discriminant::pos(std::i8::MAX as u64), }, - Bounds { + Limits { repr: U16, min: Discriminant::zero(), max: Discriminant::pos(std::u16::MAX as u64), }, - Bounds { + Limits { repr: I16, min: Discriminant::neg(std::i16::MIN as i64), max: Discriminant::pos(std::i16::MAX as u64), }, - Bounds { + Limits { repr: U32, min: Discriminant::zero(), max: Discriminant::pos(std::u32::MAX as u64), }, - Bounds { + Limits { repr: I32, min: Discriminant::neg(std::i32::MIN as i64), max: Discriminant::pos(std::i32::MAX as u64), }, - Bounds { + Limits { repr: U64, min: Discriminant::zero(), max: Discriminant::pos(std::u64::MAX), }, - Bounds { + Limits { repr: I64, min: Discriminant::neg(std::i64::MIN), max: Discriminant::pos(std::i64::MAX as u64), From 65bc8e6af849243b56e032a9ca2c52df596e10e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:30:24 +0000 Subject: [PATCH 611/2232] Detect earlier untyped discriminants out of bounds of later suffix --- diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 35ef4cb..6a04d88 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -32,7 +32,21 @@ impl DiscriminantSet { pub fn insert(&mut self, expr: &Expr) -> Result { let (discriminant, repr) = expr_to_discriminant(expr)?; match (self.repr, repr) { - (None, _) => 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)); @@ -109,18 +123,14 @@ fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option)> { fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result { if let Some(expected_repr) = set.repr { - for limits in &LIMITS { - if limits.repr != expected_repr { - continue; - } - if limits.min <= discriminant && discriminant <= limits.max { - break; + 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)); } - let msg = format!( - "discriminant value `{}` is outside the limits of {}", - discriminant, expected_repr, - ); - return Err(Error::new(Span::call_site(), msg)); } } if set.values.insert(discriminant) { @@ -230,12 +240,24 @@ fn parse_int_suffix(suffix: &str) -> Result> { 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 { + for limits in &LIMITS { + if limits.repr == repr { + return Some(*limits); + } + } + None + } +} + const LIMITS: [Limits; 8] = [ Limits { repr: U8, diff --git a/tests/ui/enum_out_of_bounds.rs b/tests/ui/enum_out_of_bounds.rs index 1aec5f0..9e0090b 100644 --- a/tests/ui/enum_out_of_bounds.rs +++ b/tests/ui/enum_out_of_bounds.rs @@ -1,9 +1,13 @@ #[cxx::bridge] mod ffi { #[repr(u32)] - enum Bad { + enum Bad1 { A = 0xFFFF_FFFF_FFFF_FFFF, } + enum Bad2 { + A = 2000, + B = 1u8, + } } fn main() {} diff --git a/tests/ui/enum_out_of_bounds.stderr b/tests/ui/enum_out_of_bounds.stderr index f437ea1..5d02e20 100644 --- a/tests/ui/enum_out_of_bounds.stderr +++ b/tests/ui/enum_out_of_bounds.stderr @@ -3,3 +3,9 @@ error: discriminant value `18446744073709551615` is outside the limits of u32 | 5 | A = 0xFFFF_FFFF_FFFF_FFFF, | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: discriminant value `2000` is outside the limits of u8 + --> $DIR/enum_out_of_bounds.rs:9:9 + | +9 | B = 1u8, + | ^^^^^^^ From 2967b66788f540a9ba98cced569c92d6f4aa121e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:53:06 +0000 Subject: [PATCH 612/2232] Move finding of bridge mod to its own module --- diff --git a/gen/src/find.rs b/gen/src/find.rs new file mode 100644 index 0000000..dc83431 --- /dev/null +++ b/gen/src/find.rs @@ -0,0 +1,36 @@ +use crate::gen::{Error, Input, Result}; +use crate::syntax::namespace::Namespace; +use quote::quote; +use syn::{Attribute, File, Item}; + +pub(super) fn find_bridge_mod(syntax: File) -> Result { + for item in syntax.items { + if let Item::Mod(item) = item { + for attr in &item.attrs { + let path = &attr.path; + if quote!(#path).to_string() == "cxx :: bridge" { + let module = match item.content { + Some(module) => module.1, + None => { + return Err(Error::Syn(syn::Error::new_spanned( + item, + Error::OutOfLineMod, + ))); + } + }; + let namespace = parse_args(attr)?; + return Ok(Input { namespace, module }); + } + } + } + } + Err(Error::NoBridgeMod) +} + +fn parse_args(attr: &Attribute) -> syn::Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 6663e4b..fa69cb5 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,6 +2,7 @@ // the cmd. mod error; +mod find; pub(super) mod include; pub(super) mod out; mod write; @@ -10,10 +11,9 @@ use self::error::{format_err, Error, Result}; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; -use quote::quote; use std::fs; use std::path::Path; -use syn::{Attribute, File, Item}; +use syn::Item; struct Input { namespace: Namespace, @@ -45,7 +45,7 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let syntax = syn::parse_file(&source)?; - let bridge = find_bridge_mod(syntax)?; + let bridge = find::find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; let ref apis = syntax::parse_items(errors, bridge.module); let ref types = Types::collect(errors, apis); @@ -59,35 +59,3 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec { Err(err) => format_err(path, &source, err), } } - -fn find_bridge_mod(syntax: File) -> Result { - for item in syntax.items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path; - if quote!(#path).to_string() == "cxx :: bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Input { namespace, module }); - } - } - } - } - Err(Error::NoBridgeMod) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} From b4dba23910d0444291159647acc4a1ac0444ecdc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 07:55:40 +0000 Subject: [PATCH 613/2232] Bring gen module doc up to date --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index fa69cb5..b9d35d7 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,5 +1,5 @@ -// Functionality that is shared between the cxx::generate_bridge entry point and -// the cmd. +// Functionality that is shared between the cxx_build::bridge entry point and +// the cxxbridge CLI command. mod error; mod find; From 2498af39f2ffe63deed13a28d0c3fc515b029812 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 08:13:33 +0000 Subject: [PATCH 614/2232] Find cxx::bridge mod nested inside another mod --- diff --git a/gen/src/find.rs b/gen/src/find.rs index dc83431..86e1dc7 100644 --- a/gen/src/find.rs +++ b/gen/src/find.rs @@ -4,7 +4,14 @@ use quote::quote; use syn::{Attribute, File, Item}; pub(super) fn find_bridge_mod(syntax: File) -> Result { - for item in syntax.items { + match scan(syntax.items)? { + Some(input) => Ok(input), + None => Err(Error::NoBridgeMod), + } +} + +fn scan(items: Vec) -> Result> { + for item in items { if let Item::Mod(item) = item { for attr in &item.attrs { let path = &attr.path; @@ -19,12 +26,17 @@ pub(super) fn find_bridge_mod(syntax: File) -> Result { } }; let namespace = parse_args(attr)?; - return Ok(Input { namespace, module }); + return Ok(Some(Input { namespace, module })); + } + } + if let Some(module) = item.content { + if let Some(input) = scan(module.1)? { + return Ok(Some(input)); } } } } - Err(Error::NoBridgeMod) + Ok(None) } fn parse_args(attr: &Attribute) -> syn::Result { From dbe7cb0721142651133befcbfa2a33f312fca6ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 08:20:00 +0000 Subject: [PATCH 615/2232] Merge pull request #197 from dtolnay/find Find cxx::bridge mod nested inside another mod --- diff --git a/gen/src/find.rs b/gen/src/find.rs index dc83431..86e1dc7 100644 --- a/gen/src/find.rs +++ b/gen/src/find.rs @@ -4,7 +4,14 @@ use quote::quote; use syn::{Attribute, File, Item}; pub(super) fn find_bridge_mod(syntax: File) -> Result { - for item in syntax.items { + match scan(syntax.items)? { + Some(input) => Ok(input), + None => Err(Error::NoBridgeMod), + } +} + +fn scan(items: Vec) -> Result> { + for item in items { if let Item::Mod(item) = item { for attr in &item.attrs { let path = &attr.path; @@ -19,12 +26,17 @@ pub(super) fn find_bridge_mod(syntax: File) -> Result { } }; let namespace = parse_args(attr)?; - return Ok(Input { namespace, module }); + return Ok(Some(Input { namespace, module })); + } + } + if let Some(module) = item.content { + if let Some(input) = scan(module.1)? { + return Ok(Some(input)); } } } } - Err(Error::NoBridgeMod) + Ok(None) } fn parse_args(attr: &Attribute) -> syn::Result { From f5ac0d9ffa6a6e531151fd2d109f541022fc5cf6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 20:19:14 +0000 Subject: [PATCH 616/2232] Fix permission denied failure in bazel CI job This job started failing in GitHub Actions with this error: ## $ bazel run demo-rs --verbose_failures --noshow_progress Downloading https://releases.bazel.build/3.1.0/release/bazel-3.1.0-linux-x86_64... could not run Bazel: could not start Bazel: fork/exec /home/runner/work/cxx/cxx/tools/bazel: permission denied ##[error]Process completed with exit code 1. --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1040454..f0ca230 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,7 @@ jobs: wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh chmod +x install.sh ./install.sh --user + echo ::add-path::$HOME/bin - name: Vendor dependencies run: | cp third-party/Cargo.lock . From 43f3aee48d693176718c8047bb87bb637fbe7ddc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 20:26:45 +0000 Subject: [PATCH 617/2232] Merge pull request #200 from dtolnay/bazel Fix permission denied failure in bazel CI job --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1040454..f0ca230 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,7 @@ jobs: wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh chmod +x install.sh ./install.sh --user + echo ::add-path::$HOME/bin - name: Vendor dependencies run: | cp third-party/Cargo.lock . From e3daefbf63c7ea9115450eb6fd7a5cc54abf01a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 20:27:04 +0000 Subject: [PATCH 618/2232] Add crosslink icons to top of rustdoc --- diff --git a/src/lib.rs b/src/lib.rs index 3b37c5a..f0e89f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,8 @@ -//! **[https://github.com/dtolnay/cxx]** +//! [![github]](https://github.com/dtolnay/cxx) [![crates-io]](https://crates.io/crates/cxx) [![docs-rs]](https://docs.rs/cxx) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K //! //!
//! From 7fbfc95720961e16c511224d62485a4f2b312f27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 11 2020 20:35:11 +0000 Subject: [PATCH 619/2232] Merge pull request #198 from dtolnay/icons Add crosslink icons to top of rustdoc --- diff --git a/src/lib.rs b/src/lib.rs index 3b37c5a..f0e89f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,8 @@ -//! **[https://github.com/dtolnay/cxx]** +//! [![github]](https://github.com/dtolnay/cxx) [![crates-io]](https://crates.io/crates/cxx) [![docs-rs]](https://docs.rs/cxx) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K //! //!
//! From bbcf2154e65a49ed9d24b35ed9aae4f448edeb22 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 02:04:25 +0000 Subject: [PATCH 620/2232] Accept bracketed in addition to quoted extra includes Invoked as: cxxbridge path/to/file.rs -i '' -i path/to/quoted Emits: #include #include "path/to/quoted" --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 129a8e6..2f030ba 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -61,7 +61,11 @@ impl Extend for Includes { impl Display for Includes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for include in &self.custom { - writeln!(f, "#include \"{}\"", include.escape_default())?; + if include.starts_with('<') && include.ends_with('>') { + writeln!(f, "#include {}", include)?; + } else { + writeln!(f, "#include \"{}\"", include.escape_default())?; + } } if self.array { writeln!(f, "#include ")?; From 891845141243f5b9ca579b2df0c6ed77bf838e0d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:05:56 +0000 Subject: [PATCH 621/2232] Pull in syn 1.0.20 for Macro::parse_body fix https://github.com/dtolnay/syn/pull/791 This will give better error message in the upcoming implementation of include!(). --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 84ab6fe..d703c57 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -15,7 +15,7 @@ cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" -syn = { version = "1.0.19", features = ["full"] } +syn = { version = "1.0.20", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index d14dc84..e8b600a 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -19,7 +19,7 @@ codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" structopt = "0.3" -syn = { version = "1.0.19", features = ["full"] } +syn = { version = "1.0.20", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 0dc3038..8326e22 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -16,7 +16,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0.4" -syn = { version = "1.0.19", features = ["full"] } +syn = { version = "1.0.20", features = ["full"] } [dev-dependencies] cxx = { version = "0.3", path = ".." } diff --git a/third-party/BUCK b/third-party/BUCK index e16bad2..d30eda0 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.19/src/**"]), + srcs = glob(["vendor/syn-1.0.20/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 45b1163..7112779 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.19/src/**"]), + srcs = glob(["vendor/syn-1.0.20/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index e54c293..f4daa91 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" +checksum = "dd1b5e337360b1fae433c59fcafa0c6b77c605e92540afa5221a7b81a9eca91d" dependencies = [ "proc-macro2", "quote", From 91e87fa51e42236006cc30e385886c56ceb2fa52 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:07:34 +0000 Subject: [PATCH 622/2232] Parse include!() For example: #[cxx::bridge] mod ffi { extern "C" { include!("path/to/quoted"); include!(); ... } } Emitted as: #include "path/to/quoted" #include --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 2f030ba..be0bd8e 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -47,8 +47,8 @@ impl Includes { Includes::default() } - pub fn insert(&mut self, include: String) { - self.custom.push(include); + pub fn insert(&mut self, include: impl AsRef) { + self.custom.push(include.as_ref().to_owned()); } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 0849d90..d5eae90 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -24,7 +24,7 @@ pub(super) fn gen( out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { - out.include.insert(include.value()); + out.include.insert(include); } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 4702a69..88c96e8 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -23,7 +23,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Expr, Lifetime, LitStr, Token, Type as RustType}; +use syn::{Expr, Lifetime, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::Derive; @@ -32,7 +32,7 @@ pub use self::parse::parse_items; pub use self::types::Types; pub enum Api { - Include(LitStr), + Include(String), Struct(Struct), Enum(Enum), CxxType(ExternType), diff --git a/syntax/parse.rs b/syntax/parse.rs index a11a5c8..a1d31cf 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -5,14 +5,14 @@ use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::TokenStream; +use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, - ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, + Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -187,7 +187,7 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), }, ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { - match foreign.mac.parse_body() { + match foreign.mac.parse_body_with(parse_include) { Ok(include) => items.push(Api::Include(include)), Err(err) => cx.push(err), } @@ -389,6 +389,38 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } +fn parse_include(input: ParseStream) -> Result { + if input.peek(LitStr) { + return Ok(input.parse::()?.value()); + } + + if input.peek(Token![<]) { + let mut path = String::new(); + input.parse::()?; + path.push('<'); + while !input.is_empty() && !input.peek(Token![>]) { + let token: TokenTree = input.parse()?; + match token { + TokenTree::Ident(token) => path += &token.to_string(), + TokenTree::Literal(token) + if token + .to_string() + .starts_with(|ch: char| ch.is_ascii_digit()) => + { + path += &token.to_string(); + } + TokenTree::Punct(token) => path.push(token.as_char()), + _ => return Err(Error::new(token.span(), "unexpected token in include path")), + } + } + input.parse::]>()?; + path.push('>'); + return Ok(path); + } + + Err(input.error("expected \"quoted/path/to\" or ")) +} + fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), From cf96664b29e873e565e404d80231ff4cfbc08fcc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:07:34 +0000 Subject: [PATCH 623/2232] Test include parsing --- diff --git a/tests/ui/include.rs b/tests/ui/include.rs new file mode 100644 index 0000000..82fa8de --- /dev/null +++ b/tests/ui/include.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + include!("path/to" what); + include!( what); + include!(); + include!(...); + } +} + +fn main() {} diff --git a/tests/ui/include.stderr b/tests/ui/include.stderr new file mode 100644 index 0000000..c85a83e --- /dev/null +++ b/tests/ui/include.stderr @@ -0,0 +1,29 @@ +error: unexpected token + --> $DIR/include.rs:4:28 + | +4 | include!("path/to" what); + | ^^^^ + +error: unexpected token + --> $DIR/include.rs:5:28 + | +5 | include!( what); + | ^^^^ + +error: expected `>` + --> $DIR/include.rs:6:17 + | +6 | include!( $DIR/include.rs:7:23 + | +7 | include!(); + | ^^^^ + +error: expected "quoted/path/to" or + --> $DIR/include.rs:8:18 + | +8 | include!(...); + | ^^^ From 14db1e2049ab565122e0a3360a3b10720dd580e0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:14:45 +0000 Subject: [PATCH 624/2232] Merge pull request #201 from dtolnay/include Parse include!() --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 2f030ba..be0bd8e 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -47,8 +47,8 @@ impl Includes { Includes::default() } - pub fn insert(&mut self, include: String) { - self.custom.push(include); + pub fn insert(&mut self, include: impl AsRef) { + self.custom.push(include.as_ref().to_owned()); } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 0849d90..d5eae90 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -24,7 +24,7 @@ pub(super) fn gen( out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { - out.include.insert(include.value()); + out.include.insert(include); } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 4702a69..88c96e8 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -23,7 +23,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Expr, Lifetime, LitStr, Token, Type as RustType}; +use syn::{Expr, Lifetime, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::Derive; @@ -32,7 +32,7 @@ pub use self::parse::parse_items; pub use self::types::Types; pub enum Api { - Include(LitStr), + Include(String), Struct(Struct), Enum(Enum), CxxType(ExternType), diff --git a/syntax/parse.rs b/syntax/parse.rs index a11a5c8..a1d31cf 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -5,14 +5,14 @@ use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::TokenStream; +use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, - ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, + Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -187,7 +187,7 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), }, ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => { - match foreign.mac.parse_body() { + match foreign.mac.parse_body_with(parse_include) { Ok(include) => items.push(Api::Include(include)), Err(err) => cx.push(err), } @@ -389,6 +389,38 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } +fn parse_include(input: ParseStream) -> Result { + if input.peek(LitStr) { + return Ok(input.parse::()?.value()); + } + + if input.peek(Token![<]) { + let mut path = String::new(); + input.parse::()?; + path.push('<'); + while !input.is_empty() && !input.peek(Token![>]) { + let token: TokenTree = input.parse()?; + match token { + TokenTree::Ident(token) => path += &token.to_string(), + TokenTree::Literal(token) + if token + .to_string() + .starts_with(|ch: char| ch.is_ascii_digit()) => + { + path += &token.to_string(); + } + TokenTree::Punct(token) => path.push(token.as_char()), + _ => return Err(Error::new(token.span(), "unexpected token in include path")), + } + } + input.parse::]>()?; + path.push('>'); + return Ok(path); + } + + Err(input.error("expected \"quoted/path/to\" or ")) +} + fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), diff --git a/tests/ui/include.rs b/tests/ui/include.rs new file mode 100644 index 0000000..82fa8de --- /dev/null +++ b/tests/ui/include.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + extern "C" { + include!("path/to" what); + include!( what); + include!(); + include!(...); + } +} + +fn main() {} diff --git a/tests/ui/include.stderr b/tests/ui/include.stderr new file mode 100644 index 0000000..c85a83e --- /dev/null +++ b/tests/ui/include.stderr @@ -0,0 +1,29 @@ +error: unexpected token + --> $DIR/include.rs:4:28 + | +4 | include!("path/to" what); + | ^^^^ + +error: unexpected token + --> $DIR/include.rs:5:28 + | +5 | include!( what); + | ^^^^ + +error: expected `>` + --> $DIR/include.rs:6:17 + | +6 | include!( $DIR/include.rs:7:23 + | +7 | include!(); + | ^^^^ + +error: expected "quoted/path/to" or + --> $DIR/include.rs:8:18 + | +8 | include!(...); + | ^^^ From 9808ef177cd75eecc30d4241cf604ff8cda2dacc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:30:44 +0000 Subject: [PATCH 625/2232] Add cargo cfgs for opt in to newer standards Currently unused, but will be needed when integrating std::optional or std::string_view support. [dependencies] cxx = { version = "0.3", features = ["c++17"] } --- diff --git a/Cargo.toml b/Cargo.toml index 829eac3..db3b5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,11 @@ exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] +[features] +default = [] # c++11 +"c++14" = [] +"c++17" = [] + [dependencies] cxxbridge-macro = { version = "=0.3.2", path = "macro" } link-cplusplus = "1.0" diff --git a/build.rs b/build.rs index 9a071fe..a412dbd 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,13 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag_if_supported("-std=c++11") + .flag_if_supported(if cfg!(feature = "c++17") { + "-std=c++17" + } else if cfg!(feature = "c++14") { + "-std=c++14" + } else { + "-std=c++11" + }) .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); From c13ad23d9c1df66f73402042114de465539c0bb7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:38:19 +0000 Subject: [PATCH 626/2232] Switch demo code to using std::make_unique --- diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK index 595a18b..f60200b 100644 --- a/demo-cxx/BUCK +++ b/demo-cxx/BUCK @@ -1,6 +1,7 @@ cxx_library( name = "demo-cxx", srcs = ["demo.cc"], + compiler_flags = ["-std=c++14"], visibility = ["PUBLIC"], deps = [ ":include", diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD index da97cfa..7b1860a 100644 --- a/demo-cxx/BUILD +++ b/demo-cxx/BUILD @@ -1,6 +1,7 @@ cc_library( name = "demo-cxx", srcs = ["demo.cc"], + copts = ["-std=c++14"], visibility = ["//visibility:public"], deps = [ ":include", diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..21bdad4 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -10,7 +10,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } std::unique_ptr make_demo(rust::Str appname) { - return std::unique_ptr(new ThingC(std::string(appname))); + return std::make_unique(std::string(appname)); } const std::string &get_name(const ThingC &thing) { return thing.appname; } diff --git a/demo-rs/build.rs b/demo-rs/build.rs index e4792c2..f32b8ef 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,7 +1,7 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") - .flag_if_supported("-std=c++11") + .flag_if_supported("-std=c++14") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); From 4ab952da41a3ef952fb922fa069b23fc9a8875b7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:43:35 +0000 Subject: [PATCH 627/2232] Merge pull request #202 from dtolnay/stdflag Add cargo cfgs for opt in to newer standards --- diff --git a/Cargo.toml b/Cargo.toml index 829eac3..db3b5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,11 @@ exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] +[features] +default = [] # c++11 +"c++14" = [] +"c++17" = [] + [dependencies] cxxbridge-macro = { version = "=0.3.2", path = "macro" } link-cplusplus = "1.0" diff --git a/build.rs b/build.rs index 9a071fe..a412dbd 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,13 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag_if_supported("-std=c++11") + .flag_if_supported(if cfg!(feature = "c++17") { + "-std=c++17" + } else if cfg!(feature = "c++14") { + "-std=c++14" + } else { + "-std=c++11" + }) .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); From 2b93b555608a9d6ed3e9461442df8ca14cc80868 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:48:12 +0000 Subject: [PATCH 628/2232] Merge pull request #203 from dtolnay/unique Switch demo code to using std::make_unique --- diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK index 595a18b..f60200b 100644 --- a/demo-cxx/BUCK +++ b/demo-cxx/BUCK @@ -1,6 +1,7 @@ cxx_library( name = "demo-cxx", srcs = ["demo.cc"], + compiler_flags = ["-std=c++14"], visibility = ["PUBLIC"], deps = [ ":include", diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD index da97cfa..7b1860a 100644 --- a/demo-cxx/BUILD +++ b/demo-cxx/BUILD @@ -1,6 +1,7 @@ cc_library( name = "demo-cxx", srcs = ["demo.cc"], + copts = ["-std=c++14"], visibility = ["//visibility:public"], deps = [ ":include", diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc index cd447ea..21bdad4 100644 --- a/demo-cxx/demo.cc +++ b/demo-cxx/demo.cc @@ -10,7 +10,7 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } std::unique_ptr make_demo(rust::Str appname) { - return std::unique_ptr(new ThingC(std::string(appname))); + return std::make_unique(std::string(appname)); } const std::string &get_name(const ThingC &thing) { return thing.appname; } diff --git a/demo-rs/build.rs b/demo-rs/build.rs index e4792c2..f32b8ef 100644 --- a/demo-rs/build.rs +++ b/demo-rs/build.rs @@ -1,7 +1,7 @@ fn main() { cxx_build::bridge("src/main.rs") .file("../demo-cxx/demo.cc") - .flag_if_supported("-std=c++11") + .flag_if_supported("-std=c++14") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); From 0e078f74be2122bff458ff23e43e364a11abaa90 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:50:03 +0000 Subject: [PATCH 629/2232] Add release notes link to crate-level rustdoc --- diff --git a/src/lib.rs b/src/lib.rs index f0e89f0..23f2d60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,8 @@ //! //!
//! -//! *Compiler support: requires rustc 1.42+* +//! *Compiler support: requires rustc 1.42+*
+//! *[Release notes](https://github.com/dtolnay/cxx/releases)* //! //!
//! From cf223856623f4f44437e2471bc66d9f90af0e66d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:50:59 +0000 Subject: [PATCH 630/2232] Document c++ standard support --- diff --git a/README.md b/README.md index 9e397f2..5fd02a9 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ can be 100% safe. cxx = "0.3" ``` -*Compiler support: requires rustc 1.42+*
+*Compiler support: requires rustc 1.42+ and c++11 or newer*
*[Release notes](https://github.com/dtolnay/cxx/releases)*
diff --git a/src/lib.rs b/src/lib.rs index 23f2d60..5ebc119 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ //! //!
//! -//! *Compiler support: requires rustc 1.42+*
+//! *Compiler support: requires rustc 1.42+ and c++11 or newer*
//! *[Release notes](https://github.com/dtolnay/cxx/releases)* //! //!
From 6aa34e4240c586647a788da3429622801c320ea9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 12 2020 03:56:51 +0000 Subject: [PATCH 631/2232] Release 0.3.3 --- diff --git a/Cargo.toml b/Cargo.toml index db3b5e1..204a1c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.2" # remember to update html_root_url +version = "0.3.3" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -19,14 +19,14 @@ default = [] # c++11 "c++17" = [] [dependencies] -cxxbridge-macro = { version = "=0.3.2", path = "macro" } +cxxbridge-macro = { version = "=0.3.3", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.2", path = "gen/build" } +cxx-build = { version = "=0.3.3", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.27", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index d703c57..98b3497 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.2" +version = "0.3.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index e8b600a..ddf27c5 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.2" +version = "0.3.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 8326e22..e4d35b8 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.2" +version = "0.3.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 5ebc119..a657af7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.2")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.3")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/BUCK b/third-party/BUCK index d30eda0..9641cd8 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -20,7 +20,7 @@ rust_library( rust_library( name = "clap", - srcs = glob(["vendor/clap-2.33.0/src/**"]), + srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", deps = [ ":bitflags", diff --git a/third-party/BUILD b/third-party/BUILD index 7112779..ec896ec 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -25,7 +25,7 @@ rust_library( rust_library( name = "clap", - srcs = glob(["vendor/clap-2.33.0/src/**"]), + srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", deps = [ ":bitflags", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f4daa91..75cfa84 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -40,9 +40,9 @@ checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term", "atty", @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.2" +version = "0.3.3" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.2" +version = "0.3.3" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.2" +version = "0.3.3" dependencies = [ "anyhow", "codespan-reporting", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.2" +version = "0.3.3" dependencies = [ "cxx", "proc-macro2", @@ -246,18 +246,18 @@ checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "serde" -version = "1.0.107" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba7550f2cdf88ffc23ab0f1607133486c390a8c0f89b57e589b9654ee15e04d" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.107" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10be45e22e5597d4b88afcc71f9d7bfadcd604bf0c78a3ab4582b8d2b37f39f3" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ "proc-macro2", "quote", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", From 32ee9d3676ed272138f8a9388567b0f17657f129 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:01:15 +0000 Subject: [PATCH 632/2232] rust::Vec has a bitcopy constructor too --- diff --git a/gen/src/write.rs b/gen/src/write.rs index d5eae90..12f2839 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -244,7 +244,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "// #include \"rust/cxx.h\""); } - if needs_rust_string { + if needs_rust_string || needs_rust_vec { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } From 8c54eeca6ce3b73f545c8a8dc1735a7690a70fe8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:08:20 +0000 Subject: [PATCH 633/2232] Add missing noexcept on a Slice constructor --- diff --git a/include/cxx.h b/include/cxx.h index d870442..8525313 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -98,7 +98,7 @@ public: Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Slice(const Slice &) noexcept = default; - Slice(const T *s, size_t size) : repr(Repr{s, size}) {} + Slice(const T *s, size_t size) noexcept : repr(Repr{s, size}) {} Slice &operator=(Slice other) noexcept { this->repr = other.repr; From 1385ca42b2f94cd83b9556452316b5976842da35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:15:20 +0000 Subject: [PATCH 634/2232] Add missing noexcept on Vec::const_iterator methods --- diff --git a/include/cxx.h b/include/cxx.h index 8525313..b56c14a 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -243,21 +243,25 @@ public: typename std::add_const::type>::type; using iterator_category = std::forward_iterator_tag; - const T &operator*() const { return *static_cast(this->pos); } - const T *operator->() const { return static_cast(this->pos); } - const_iterator &operator++() { + const T &operator*() const noexcept { + return *static_cast(this->pos); + } + const T *operator->() const noexcept { + return static_cast(this->pos); + } + const_iterator &operator++() noexcept { this->pos = static_cast(this->pos) + this->stride; return *this; } - const_iterator operator++(int) { + const_iterator operator++(int) noexcept { auto ret = const_iterator(*this); this->pos = static_cast(this->pos) + this->stride; return ret; } - bool operator==(const const_iterator &other) const { + bool operator==(const const_iterator &other) const noexcept { return this->pos == other.pos; } - bool operator!=(const const_iterator &other) const { + bool operator!=(const const_iterator &other) const noexcept { return this->pos != other.pos; } From e54f338188c61d6fdddfb8a6454eb786f2bc4201 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:43:20 +0000 Subject: [PATCH 635/2232] Support multiple cxx.h header sections for the same guard --- diff --git a/gen/src/include.rs b/gen/src/include.rs index be0bd8e..0ed76ac 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,21 +1,42 @@ +use crate::gen::out::OutFile; use std::fmt::{self, Display}; pub static HEADER: &str = include_str!("include/cxx.h"); -pub fn get(guard: &str) -> &'static str { +pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) { let ifndef = format!("#ifndef {}", guard); + let define = format!("#define {}", guard); let endif = format!("#endif // {}", guard); - let begin = find_line(&ifndef); - let end = find_line(&endif); - if let (Some(begin), Some(end)) = (begin, end) { - &HEADER[begin..end + endif.len()] - } else { - panic!("not found in cxx.h header: {}", guard) + + let mut offset = 0; + loop { + let begin = find_line(offset, &ifndef); + let end = find_line(offset, &endif); + if let (Some(begin), Some(end)) = (begin, end) { + if !needed { + return; + } + out.next_section(); + if offset == 0 { + writeln!(out, "{}", ifndef); + writeln!(out, "{}", define); + } + for line in HEADER[begin + ifndef.len()..end].trim().lines() { + if line != define && !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + offset = end + endif.len(); + } else if offset == 0 { + panic!("not found in cxx.h header: {}", guard) + } else { + writeln!(out, "{}", endif); + return; + } } } -fn find_line(line: &str) -> Option { - let mut offset = 0; +fn find_line(mut offset: usize, line: &str) -> Option { loop { offset += HEADER[offset..].find(line)?; let rest = &HEADER[offset + line.len()..]; diff --git a/gen/src/write.rs b/gen/src/write.rs index 12f2839..c06fce9 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -249,15 +249,15 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "struct unsafe_bitcopy_t;"); } - write_header_section(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); - write_header_section(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); - write_header_section(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); - write_header_section(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); - write_header_section(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); - write_header_section(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); - write_header_section(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); - write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); + include::write(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); + include::write(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); + include::write(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); + include::write(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); + include::write(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); + include::write(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); + include::write(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); + include::write(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); + include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); @@ -311,18 +311,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { - let section = include::get(section); - if needed { - out.next_section(); - for line in section.lines() { - if !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - } -} - fn write_struct(out: &mut OutFile, strct: &Struct) { for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); From 2a2b9ad1f527c45b237cef8757ee20bb7b790282 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:43:20 +0000 Subject: [PATCH 636/2232] Move implementation details to the bottom of cxx.h --- diff --git a/.clang-format b/.clang-format index 181605d..2085997 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,2 @@ AlwaysBreakTemplateDeclarations: true +MaxEmptyLinesToKeep: 3 diff --git a/include/cxx.h b/include/cxx.h index b56c14a..9f4fb47 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,13 +15,7 @@ namespace rust { inline namespace cxxbridge03 { -#ifndef CXXBRIDGE03_RUST_BITCOPY -#define CXXBRIDGE03_RUST_BITCOPY -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE03_RUST_BITCOPY +struct unsafe_bitcopy_t; #ifndef CXXBRIDGE03_RUST_STRING #define CXXBRIDGE03_RUST_STRING @@ -91,23 +85,18 @@ private: #endif // CXXBRIDGE03_RUST_STR #ifndef CXXBRIDGE03_RUST_SLICE -#define CXXBRIDGE03_RUST_SLICE template class Slice final { public: - Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} - Slice(const Slice &) noexcept = default; - - Slice(const T *s, size_t size) noexcept : repr(Repr{s, size}) {} + Slice() noexcept; + Slice(const Slice &) noexcept; + Slice(const T *, size_t count) noexcept; - Slice &operator=(Slice other) noexcept { - this->repr = other.repr; - return *this; - } + Slice &operator=(Slice) noexcept; - const T *data() const noexcept { return this->repr.ptr; } - size_t size() const noexcept { return this->repr.len; } - size_t length() const noexcept { return this->repr.len; } + const T *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; // Repr is PRIVATE; must not be used other than by our generated code. // @@ -118,8 +107,8 @@ public: const T *ptr; size_t len; }; - Slice(Repr repr_) noexcept : repr(repr_) {} - explicit operator Repr() noexcept { return this->repr; } + Slice(Repr) noexcept; + explicit operator Repr() noexcept; private: Repr repr; @@ -127,7 +116,6 @@ private: #endif // CXXBRIDGE03_RUST_SLICE #ifndef CXXBRIDGE03_RUST_BOX -#define CXXBRIDGE03_RUST_BOX template class Box final { public: @@ -136,70 +124,32 @@ public: typename std::add_pointer::type>::type; using pointer = typename std::add_pointer::type; - Box(const Box &other) : Box(*other) {} - Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - explicit Box(const T &val) { - this->uninit(); - ::new (this->ptr) T(val); - } - explicit Box(T &&val) { - this->uninit(); - ::new (this->ptr) T(std::move(val)); - } - Box &operator=(const Box &other) { - if (this != &other) { - if (this->ptr) { - **this = *other; - } else { - this->uninit(); - ::new (this->ptr) T(*other); - } - } - return *this; - } - Box &operator=(Box &&other) noexcept { - if (this->ptr) { - this->drop(); - } - this->ptr = other.ptr; - other.ptr = nullptr; - return *this; - } - ~Box() noexcept { - if (this->ptr) { - this->drop(); - } - } + Box(const Box &); + Box(Box &&) noexcept; + ~Box() noexcept; - const T *operator->() const noexcept { return this->ptr; } - const T &operator*() const noexcept { return *this->ptr; } - T *operator->() noexcept { return this->ptr; } - T &operator*() noexcept { return *this->ptr; } + explicit Box(const T &); + explicit Box(T &&); + + Box &operator=(const Box &); + Box &operator=(Box &&) noexcept; + + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; template - static Box in_place(Fields &&... fields) { - Box box; - box.uninit(); - ::new (box.ptr) T{std::forward(fields)...}; - return box; - } + static Box in_place(Fields &&...); // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. - static Box from_raw(T *raw) noexcept { - Box box; - box.ptr = raw; - return box; - } + static Box from_raw(T *) noexcept; - T *into_raw() noexcept { - T *raw = this->ptr; - this->ptr = nullptr; - return raw; - } + T *into_raw() noexcept; private: - Box() noexcept {} + Box() noexcept; void uninit() noexcept; void drop() noexcept; T *ptr; @@ -207,30 +157,19 @@ private: #endif // CXXBRIDGE03_RUST_BOX #ifndef CXXBRIDGE03_RUST_VEC -#define CXXBRIDGE03_RUST_VEC template class Vec final { public: using value_type = T; Vec() noexcept; - Vec(Vec &&other) noexcept { - this->repr = other.repr; - new (&other) Vec(); - } - ~Vec() noexcept { this->drop(); } + Vec(Vec &&) noexcept; + ~Vec() noexcept; - Vec &operator=(Vec &&other) noexcept { - if (this != &other) { - this->drop(); - this->repr = other.repr; - new (&other) Vec(); - } - return *this; - } + Vec &operator=(Vec &&) noexcept; size_t size() const noexcept; - bool empty() const noexcept { return size() == 0; } + bool empty() const noexcept; const T *data() const noexcept; class const_iterator { @@ -243,27 +182,12 @@ public: typename std::add_const::type>::type; using iterator_category = std::forward_iterator_tag; - const T &operator*() const noexcept { - return *static_cast(this->pos); - } - const T *operator->() const noexcept { - return static_cast(this->pos); - } - const_iterator &operator++() noexcept { - this->pos = static_cast(this->pos) + this->stride; - return *this; - } - const_iterator operator++(int) noexcept { - auto ret = const_iterator(*this); - this->pos = static_cast(this->pos) + this->stride; - return ret; - } - bool operator==(const const_iterator &other) const noexcept { - return this->pos == other.pos; - } - bool operator!=(const const_iterator &other) const noexcept { - return this->pos != other.pos; - } + const T &operator*() const noexcept; + const T *operator->() const noexcept; + const_iterator &operator++() noexcept; + const_iterator operator++(int) noexcept; + bool operator==(const const_iterator &) const noexcept; + bool operator!=(const const_iterator &) const noexcept; private: friend class Vec; @@ -271,20 +195,11 @@ public: size_t stride; }; - const_iterator begin() const noexcept { - const_iterator it; - it.pos = this->data(); - it.stride = this->stride(); - return it; - } - const_iterator end() const noexcept { - const_iterator it = this->begin(); - it.pos = static_cast(it.pos) + it.stride * this->size(); - return it; - } + const_iterator begin() const noexcept; + const_iterator end() const noexcept; // Internal API only intended for the cxxbridge code generator. - Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} + Vec(unsafe_bitcopy_t, const Vec &) noexcept; private: static size_t stride() noexcept; @@ -353,6 +268,11 @@ using fn = Fn; template using try_fn = TryFn; + + +//////////////////////////////////////////////////////////////////////////////// +/// end public API, begin implementation details + template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); @@ -363,5 +283,238 @@ Fn Fn::operator*() const noexcept { return *this; } +#ifndef CXXBRIDGE03_RUST_BITCOPY +#define CXXBRIDGE03_RUST_BITCOPY +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; + +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE03_RUST_BITCOPY + +#ifndef CXXBRIDGE03_RUST_SLICE +#define CXXBRIDGE03_RUST_SLICE +template +Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + +template +Slice::Slice(const Slice &) noexcept = default; + +template +Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} + +template +Slice &Slice::operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; +} + +template +const T *Slice::data() const noexcept { + return this->repr.ptr; +} + +template +size_t Slice::size() const noexcept { + return this->repr.len; +} + +template +size_t Slice::length() const noexcept { + return this->repr.len; +} + +template +Slice::Slice(Repr repr_) noexcept : repr(repr_) {} + +template +Slice::operator Repr() noexcept { + return this->repr; +} +#endif // CXXBRIDGE03_RUST_SLICE + +#ifndef CXXBRIDGE03_RUST_BOX +#define CXXBRIDGE03_RUST_BOX +template +Box::Box(const Box &other) : Box(*other) {} + +template +Box::Box(Box &&other) noexcept : ptr(other.ptr) { + other.ptr = nullptr; +} + +template +Box::Box(const T &val) { + this->uninit(); + ::new (this->ptr) T(val); +} + +template +Box::Box(T &&val) { + this->uninit(); + ::new (this->ptr) T(std::move(val)); +} + +template +Box::~Box() noexcept { + if (this->ptr) { + this->drop(); + } +} + +template +Box &Box::operator=(const Box &other) { + if (this != &other) { + if (this->ptr) { + **this = *other; + } else { + this->uninit(); + ::new (this->ptr) T(*other); + } + } + return *this; +} + +template +Box &Box::operator=(Box &&other) noexcept { + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; +} + +template +const T *Box::operator->() const noexcept { + return this->ptr; +} + +template +const T &Box::operator*() const noexcept { + return *this->ptr; +} + +template +T *Box::operator->() noexcept { + return this->ptr; +} + +template +T &Box::operator*() noexcept { + return *this->ptr; +} + +template +template +Box Box::in_place(Fields &&... fields) { + Box box; + box.uninit(); + ::new (box.ptr) T{std::forward(fields)...}; + return box; +} + +template +Box Box::from_raw(T *raw) noexcept { + Box box; + box.ptr = raw; + return box; +} + +template +T *Box::into_raw() noexcept { + T *raw = this->ptr; + this->ptr = nullptr; + return raw; +} + +template +Box::Box() noexcept {} +#endif // CXXBRIDGE03_RUST_BOX + +#ifndef CXXBRIDGE03_RUST_VEC +#define CXXBRIDGE03_RUST_VEC +template +Vec::Vec(Vec &&other) noexcept { + this->repr = other.repr; + new (&other) Vec(); +} + +template +Vec::~Vec() noexcept { + this->drop(); +} + +template +Vec &Vec::operator=(Vec &&other) noexcept { + if (this != &other) { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + } + return *this; +} + +template +bool Vec::empty() const noexcept { + return size() == 0; +} + +template +const T &Vec::const_iterator::operator*() const noexcept { + return *static_cast(this->pos); +} + +template +const T *Vec::const_iterator::operator->() const noexcept { + return static_cast(this->pos); +} + +template +typename Vec::const_iterator &Vec::const_iterator::operator++() noexcept { + this->pos = static_cast(this->pos) + this->stride; + return *this; +} + +template +typename Vec::const_iterator +Vec::const_iterator::operator++(int) noexcept { + auto ret = const_iterator(*this); + this->pos = static_cast(this->pos) + this->stride; + return ret; +} + +template +bool Vec::const_iterator::operator==(const const_iterator &other) const + noexcept { + return this->pos == other.pos; +} + +template +bool Vec::const_iterator::operator!=(const const_iterator &other) const + noexcept { + return this->pos != other.pos; +} + +template +typename Vec::const_iterator Vec::begin() const noexcept { + const_iterator it; + it.pos = this->data(); + it.stride = this->stride(); + return it; +} + +template +typename Vec::const_iterator Vec::end() const noexcept { + const_iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; +} + +// Internal API only intended for the cxxbridge code generator. +template +Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE03_RUST_VEC + } // namespace cxxbridge03 } // namespace rust From 9846d08d40d69ca36be4d4b6b757e80b596d7079 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 03:59:17 +0000 Subject: [PATCH 637/2232] Merge pull request #206 from dtolnay/include Move all function implementations to the bottom of cxx.h --- diff --git a/.clang-format b/.clang-format index 181605d..2085997 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,2 @@ AlwaysBreakTemplateDeclarations: true +MaxEmptyLinesToKeep: 3 diff --git a/gen/src/include.rs b/gen/src/include.rs index be0bd8e..0ed76ac 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,21 +1,42 @@ +use crate::gen::out::OutFile; use std::fmt::{self, Display}; pub static HEADER: &str = include_str!("include/cxx.h"); -pub fn get(guard: &str) -> &'static str { +pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) { let ifndef = format!("#ifndef {}", guard); + let define = format!("#define {}", guard); let endif = format!("#endif // {}", guard); - let begin = find_line(&ifndef); - let end = find_line(&endif); - if let (Some(begin), Some(end)) = (begin, end) { - &HEADER[begin..end + endif.len()] - } else { - panic!("not found in cxx.h header: {}", guard) + + let mut offset = 0; + loop { + let begin = find_line(offset, &ifndef); + let end = find_line(offset, &endif); + if let (Some(begin), Some(end)) = (begin, end) { + if !needed { + return; + } + out.next_section(); + if offset == 0 { + writeln!(out, "{}", ifndef); + writeln!(out, "{}", define); + } + for line in HEADER[begin + ifndef.len()..end].trim().lines() { + if line != define && !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + offset = end + endif.len(); + } else if offset == 0 { + panic!("not found in cxx.h header: {}", guard) + } else { + writeln!(out, "{}", endif); + return; + } } } -fn find_line(line: &str) -> Option { - let mut offset = 0; +fn find_line(mut offset: usize, line: &str) -> Option { loop { offset += HEADER[offset..].find(line)?; let rest = &HEADER[offset + line.len()..]; diff --git a/gen/src/write.rs b/gen/src/write.rs index 12f2839..c06fce9 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -249,15 +249,15 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "struct unsafe_bitcopy_t;"); } - write_header_section(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); - write_header_section(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); - write_header_section(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); - write_header_section(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); - write_header_section(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); - write_header_section(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); - write_header_section(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); - write_header_section(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); - write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); + include::write(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); + include::write(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); + include::write(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); + include::write(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); + include::write(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); + include::write(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); + include::write(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); + include::write(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); + include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); @@ -311,18 +311,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_header_section(out: &mut OutFile, needed: bool, section: &str) { - let section = include::get(section); - if needed { - out.next_section(); - for line in section.lines() { - if !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - } -} - fn write_struct(out: &mut OutFile, strct: &Struct) { for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); diff --git a/include/cxx.h b/include/cxx.h index b56c14a..9f4fb47 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,13 +15,7 @@ namespace rust { inline namespace cxxbridge03 { -#ifndef CXXBRIDGE03_RUST_BITCOPY -#define CXXBRIDGE03_RUST_BITCOPY -struct unsafe_bitcopy_t { - explicit unsafe_bitcopy_t() = default; -}; -constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE03_RUST_BITCOPY +struct unsafe_bitcopy_t; #ifndef CXXBRIDGE03_RUST_STRING #define CXXBRIDGE03_RUST_STRING @@ -91,23 +85,18 @@ private: #endif // CXXBRIDGE03_RUST_STR #ifndef CXXBRIDGE03_RUST_SLICE -#define CXXBRIDGE03_RUST_SLICE template class Slice final { public: - Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} - Slice(const Slice &) noexcept = default; - - Slice(const T *s, size_t size) noexcept : repr(Repr{s, size}) {} + Slice() noexcept; + Slice(const Slice &) noexcept; + Slice(const T *, size_t count) noexcept; - Slice &operator=(Slice other) noexcept { - this->repr = other.repr; - return *this; - } + Slice &operator=(Slice) noexcept; - const T *data() const noexcept { return this->repr.ptr; } - size_t size() const noexcept { return this->repr.len; } - size_t length() const noexcept { return this->repr.len; } + const T *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; // Repr is PRIVATE; must not be used other than by our generated code. // @@ -118,8 +107,8 @@ public: const T *ptr; size_t len; }; - Slice(Repr repr_) noexcept : repr(repr_) {} - explicit operator Repr() noexcept { return this->repr; } + Slice(Repr) noexcept; + explicit operator Repr() noexcept; private: Repr repr; @@ -127,7 +116,6 @@ private: #endif // CXXBRIDGE03_RUST_SLICE #ifndef CXXBRIDGE03_RUST_BOX -#define CXXBRIDGE03_RUST_BOX template class Box final { public: @@ -136,70 +124,32 @@ public: typename std::add_pointer::type>::type; using pointer = typename std::add_pointer::type; - Box(const Box &other) : Box(*other) {} - Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } - explicit Box(const T &val) { - this->uninit(); - ::new (this->ptr) T(val); - } - explicit Box(T &&val) { - this->uninit(); - ::new (this->ptr) T(std::move(val)); - } - Box &operator=(const Box &other) { - if (this != &other) { - if (this->ptr) { - **this = *other; - } else { - this->uninit(); - ::new (this->ptr) T(*other); - } - } - return *this; - } - Box &operator=(Box &&other) noexcept { - if (this->ptr) { - this->drop(); - } - this->ptr = other.ptr; - other.ptr = nullptr; - return *this; - } - ~Box() noexcept { - if (this->ptr) { - this->drop(); - } - } + Box(const Box &); + Box(Box &&) noexcept; + ~Box() noexcept; - const T *operator->() const noexcept { return this->ptr; } - const T &operator*() const noexcept { return *this->ptr; } - T *operator->() noexcept { return this->ptr; } - T &operator*() noexcept { return *this->ptr; } + explicit Box(const T &); + explicit Box(T &&); + + Box &operator=(const Box &); + Box &operator=(Box &&) noexcept; + + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; template - static Box in_place(Fields &&... fields) { - Box box; - box.uninit(); - ::new (box.ptr) T{std::forward(fields)...}; - return box; - } + static Box in_place(Fields &&...); // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. - static Box from_raw(T *raw) noexcept { - Box box; - box.ptr = raw; - return box; - } + static Box from_raw(T *) noexcept; - T *into_raw() noexcept { - T *raw = this->ptr; - this->ptr = nullptr; - return raw; - } + T *into_raw() noexcept; private: - Box() noexcept {} + Box() noexcept; void uninit() noexcept; void drop() noexcept; T *ptr; @@ -207,30 +157,19 @@ private: #endif // CXXBRIDGE03_RUST_BOX #ifndef CXXBRIDGE03_RUST_VEC -#define CXXBRIDGE03_RUST_VEC template class Vec final { public: using value_type = T; Vec() noexcept; - Vec(Vec &&other) noexcept { - this->repr = other.repr; - new (&other) Vec(); - } - ~Vec() noexcept { this->drop(); } + Vec(Vec &&) noexcept; + ~Vec() noexcept; - Vec &operator=(Vec &&other) noexcept { - if (this != &other) { - this->drop(); - this->repr = other.repr; - new (&other) Vec(); - } - return *this; - } + Vec &operator=(Vec &&) noexcept; size_t size() const noexcept; - bool empty() const noexcept { return size() == 0; } + bool empty() const noexcept; const T *data() const noexcept; class const_iterator { @@ -243,27 +182,12 @@ public: typename std::add_const::type>::type; using iterator_category = std::forward_iterator_tag; - const T &operator*() const noexcept { - return *static_cast(this->pos); - } - const T *operator->() const noexcept { - return static_cast(this->pos); - } - const_iterator &operator++() noexcept { - this->pos = static_cast(this->pos) + this->stride; - return *this; - } - const_iterator operator++(int) noexcept { - auto ret = const_iterator(*this); - this->pos = static_cast(this->pos) + this->stride; - return ret; - } - bool operator==(const const_iterator &other) const noexcept { - return this->pos == other.pos; - } - bool operator!=(const const_iterator &other) const noexcept { - return this->pos != other.pos; - } + const T &operator*() const noexcept; + const T *operator->() const noexcept; + const_iterator &operator++() noexcept; + const_iterator operator++(int) noexcept; + bool operator==(const const_iterator &) const noexcept; + bool operator!=(const const_iterator &) const noexcept; private: friend class Vec; @@ -271,20 +195,11 @@ public: size_t stride; }; - const_iterator begin() const noexcept { - const_iterator it; - it.pos = this->data(); - it.stride = this->stride(); - return it; - } - const_iterator end() const noexcept { - const_iterator it = this->begin(); - it.pos = static_cast(it.pos) + it.stride * this->size(); - return it; - } + const_iterator begin() const noexcept; + const_iterator end() const noexcept; // Internal API only intended for the cxxbridge code generator. - Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} + Vec(unsafe_bitcopy_t, const Vec &) noexcept; private: static size_t stride() noexcept; @@ -353,6 +268,11 @@ using fn = Fn; template using try_fn = TryFn; + + +//////////////////////////////////////////////////////////////////////////////// +/// end public API, begin implementation details + template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); @@ -363,5 +283,238 @@ Fn Fn::operator*() const noexcept { return *this; } +#ifndef CXXBRIDGE03_RUST_BITCOPY +#define CXXBRIDGE03_RUST_BITCOPY +struct unsafe_bitcopy_t { + explicit unsafe_bitcopy_t() = default; +}; + +constexpr unsafe_bitcopy_t unsafe_bitcopy{}; +#endif // CXXBRIDGE03_RUST_BITCOPY + +#ifndef CXXBRIDGE03_RUST_SLICE +#define CXXBRIDGE03_RUST_SLICE +template +Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} + +template +Slice::Slice(const Slice &) noexcept = default; + +template +Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} + +template +Slice &Slice::operator=(Slice other) noexcept { + this->repr = other.repr; + return *this; +} + +template +const T *Slice::data() const noexcept { + return this->repr.ptr; +} + +template +size_t Slice::size() const noexcept { + return this->repr.len; +} + +template +size_t Slice::length() const noexcept { + return this->repr.len; +} + +template +Slice::Slice(Repr repr_) noexcept : repr(repr_) {} + +template +Slice::operator Repr() noexcept { + return this->repr; +} +#endif // CXXBRIDGE03_RUST_SLICE + +#ifndef CXXBRIDGE03_RUST_BOX +#define CXXBRIDGE03_RUST_BOX +template +Box::Box(const Box &other) : Box(*other) {} + +template +Box::Box(Box &&other) noexcept : ptr(other.ptr) { + other.ptr = nullptr; +} + +template +Box::Box(const T &val) { + this->uninit(); + ::new (this->ptr) T(val); +} + +template +Box::Box(T &&val) { + this->uninit(); + ::new (this->ptr) T(std::move(val)); +} + +template +Box::~Box() noexcept { + if (this->ptr) { + this->drop(); + } +} + +template +Box &Box::operator=(const Box &other) { + if (this != &other) { + if (this->ptr) { + **this = *other; + } else { + this->uninit(); + ::new (this->ptr) T(*other); + } + } + return *this; +} + +template +Box &Box::operator=(Box &&other) noexcept { + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; +} + +template +const T *Box::operator->() const noexcept { + return this->ptr; +} + +template +const T &Box::operator*() const noexcept { + return *this->ptr; +} + +template +T *Box::operator->() noexcept { + return this->ptr; +} + +template +T &Box::operator*() noexcept { + return *this->ptr; +} + +template +template +Box Box::in_place(Fields &&... fields) { + Box box; + box.uninit(); + ::new (box.ptr) T{std::forward(fields)...}; + return box; +} + +template +Box Box::from_raw(T *raw) noexcept { + Box box; + box.ptr = raw; + return box; +} + +template +T *Box::into_raw() noexcept { + T *raw = this->ptr; + this->ptr = nullptr; + return raw; +} + +template +Box::Box() noexcept {} +#endif // CXXBRIDGE03_RUST_BOX + +#ifndef CXXBRIDGE03_RUST_VEC +#define CXXBRIDGE03_RUST_VEC +template +Vec::Vec(Vec &&other) noexcept { + this->repr = other.repr; + new (&other) Vec(); +} + +template +Vec::~Vec() noexcept { + this->drop(); +} + +template +Vec &Vec::operator=(Vec &&other) noexcept { + if (this != &other) { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + } + return *this; +} + +template +bool Vec::empty() const noexcept { + return size() == 0; +} + +template +const T &Vec::const_iterator::operator*() const noexcept { + return *static_cast(this->pos); +} + +template +const T *Vec::const_iterator::operator->() const noexcept { + return static_cast(this->pos); +} + +template +typename Vec::const_iterator &Vec::const_iterator::operator++() noexcept { + this->pos = static_cast(this->pos) + this->stride; + return *this; +} + +template +typename Vec::const_iterator +Vec::const_iterator::operator++(int) noexcept { + auto ret = const_iterator(*this); + this->pos = static_cast(this->pos) + this->stride; + return ret; +} + +template +bool Vec::const_iterator::operator==(const const_iterator &other) const + noexcept { + return this->pos == other.pos; +} + +template +bool Vec::const_iterator::operator!=(const const_iterator &other) const + noexcept { + return this->pos != other.pos; +} + +template +typename Vec::const_iterator Vec::begin() const noexcept { + const_iterator it; + it.pos = this->data(); + it.stride = this->stride(); + return it; +} + +template +typename Vec::const_iterator Vec::end() const noexcept { + const_iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; +} + +// Internal API only intended for the cxxbridge code generator. +template +Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE03_RUST_VEC + } // namespace cxxbridge03 } // namespace rust From dd3af090b78e861a786a6d9056ce60a0b6aa6854 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 13 2020 05:19:46 +0000 Subject: [PATCH 638/2232] Try out new style of readme badges --- diff --git a/README.md b/README.md index 5fd02a9..768b394 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ CXX — safe FFI between Rust and C++ ========================================= -[![Build Status](https://img.shields.io/github/workflow/status/dtolnay/cxx/CI/master)](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster) -[![Latest Version](https://img.shields.io/crates/v/cxx.svg)](https://crates.io/crates/cxx) -[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/cxx) +[github](https://github.com/dtolnay/cxx) +[crates.io](https://crates.io/crates/cxx) +[docs.rs](https://docs.rs/cxx) +[build status](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster) This library provides a **safe** mechanism for calling C++ code from Rust and Rust code from C++, not subject to the many ways that things can go wrong when From d963bf97b78a53a09a034eb635f27d285b914387 Mon Sep 17 00:00:00 2001 From: Myron Ahn Date: May 17 2020 05:32:42 +0000 Subject: [PATCH 639/2232] Fix for when std::os::raw::c_char is u8 (armv7) --- diff --git a/tests/test.rs b/tests/test.rs index ddf8f70..91bd25e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -141,7 +141,7 @@ fn test_c_call_r() { } let failure = unsafe { cxx_run_test() }; if !failure.is_null() { - let msg = unsafe { CStr::from_ptr(failure) }; + let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) }; eprintln!("{}", msg.to_string_lossy()); } } From ecafa897c1f54cbc69c79ae045fe4150d8e8af44 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 17 2020 07:35:49 +0000 Subject: [PATCH 640/2232] Merge pull request #209 from myronahn/master Fix for when std::os::raw::c_char is u8 (armv7) --- diff --git a/tests/test.rs b/tests/test.rs index ddf8f70..91bd25e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -141,7 +141,7 @@ fn test_c_call_r() { } let failure = unsafe { cxx_run_test() }; if !failure.is_null() { - let msg = unsafe { CStr::from_ptr(failure) }; + let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) }; eprintln!("{}", msg.to_string_lossy()); } } From ef8fd19a3bbb5098ea13021073daf9a840c6bcc1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 22 2020 08:27:30 +0000 Subject: [PATCH 641/2232] Account for character based offsets in proc-macro2 --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 98b3497..a5878b9 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,7 +13,7 @@ categories = ["development-tools::ffi"] anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.12", features = ["span-locations"] } +proc-macro2 = { version = "1.0.14", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0.20", features = ["full"] } diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index ddf27c5..919d5c8 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [dependencies] anyhow = "1.0" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.12", features = ["span-locations"] } +proc-macro2 = { version = "1.0.14", features = ["span-locations"] } quote = "1.0" structopt = "0.3" syn = { version = "1.0.20", features = ["full"] } diff --git a/gen/src/error.rs b/gen/src/error.rs index b91c166..51dbbe7 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -88,17 +88,26 @@ fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, err for _ in 1..start.line { start_offset += source[start_offset..].find('\n').unwrap() + 1; } - start_offset += start.column; + let start_column = source[start_offset..] + .chars() + .take(start.column) + .map(char::len_utf8) + .sum::(); + start_offset += start_column; let mut end_offset = start_offset; if start.line == end.line { - end_offset -= start.column; + end_offset -= start_column; } else { for _ in 0..end.line - start.line { end_offset += source[end_offset..].find('\n').unwrap() + 1; } } - end_offset += end.column; + end_offset += source[end_offset..] + .chars() + .take(end.column) + .map(char::len_utf8) + .sum::(); let mut files = SimpleFiles::new(); let file = files.add(path.to_string_lossy(), source); diff --git a/third-party/BUCK b/third-party/BUCK index 9641cd8..cc64883 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.12/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.14/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", diff --git a/third-party/BUILD b/third-party/BUILD index ec896ec..313481e 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.12/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.14/src/**"]), crate_features = [ "proc-macro", "span-locations", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 75cfa84..6e3a5b9 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +checksum = "de40dd4ff82d9c9bab6dae29dbab1167e515f8df9ed17d2987cb6012db206933" dependencies = [ "unicode-xid", ] From e3b78ea4092bd52202168fd27ee888259e5f3a1e Mon Sep 17 00:00:00 2001 From: myronahn Date: May 22 2020 18:08:13 +0000 Subject: [PATCH 642/2232] Fix issue with indirect return for C++ member function w/no args --- diff --git a/gen/src/write.rs b/gen/src/write.rs index c06fce9..2cf9faf 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -424,7 +424,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { } let indirect_return = indirect_return(efn, types); if indirect_return { - if !efn.args.is_empty() { + if !efn.args.is_empty() || efn.receiver.is_some() { write!(out, ", "); } write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); diff --git a/src/exception.rs b/src/exception.rs index 125e484..e16916a 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Debug, Display}; /// Exception thrown from an `extern "C"` function. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Exception { pub(crate) what: Box, } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d4e96fc..51716e0 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -83,6 +83,8 @@ pub mod ffi { fn set(self: &mut C, n: usize) -> usize; fn get2(&self) -> usize; fn set2(&mut self, n: usize) -> usize; + fn set_succeed(&mut self, n: usize) -> Result; + fn get_fail(&mut self) -> Result; } extern "C" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index af7a4a0..677a55d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -28,6 +28,10 @@ size_t C::set2(size_t n) { return this->n; } +size_t C::set_succeed(size_t n) { return this->set2(n); } + +size_t C::get_fail() { throw std::runtime_error("unimplemented"); } + const std::vector &C::get_v() const { return this->v; } size_t c_return_primitive() { return 2020; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 1173a0a..c1a08d2 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -16,6 +16,8 @@ public: size_t set(size_t n); size_t get2() const; size_t set2(size_t n); + size_t set_succeed(size_t n); + size_t get_fail(); const std::vector &get_v() const; private: diff --git a/tests/test.rs b/tests/test.rs index 91bd25e..836e2d3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -157,7 +157,9 @@ fn test_c_method_calls() { assert_eq!(2021, unique_ptr.set(2021)); assert_eq!(2021, unique_ptr.get()); assert_eq!(old_value, unique_ptr.set2(old_value)); - assert_eq!(old_value, unique_ptr.get2()) + assert_eq!(old_value, unique_ptr.get2()); + assert_eq!(Ok(2022), unique_ptr.set_succeed(2022)); + assert!(unique_ptr.get_fail().is_err()); } #[test] From eedf737d91722868277c0aadc34e5a2c880ed00a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 22 2020 18:08:50 +0000 Subject: [PATCH 643/2232] Revert exception change from PR 210 --- diff --git a/src/exception.rs b/src/exception.rs index e16916a..125e484 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Debug, Display}; /// Exception thrown from an `extern "C"` function. -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct Exception { pub(crate) what: Box, } From ae7143655df336a0bd44f7f4c41d93142298df33 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 22 2020 18:12:35 +0000 Subject: [PATCH 644/2232] Test c method calls without needing PartialEq on Exception --- diff --git a/tests/test.rs b/tests/test.rs index 836e2d3..ecfec84 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -158,7 +158,7 @@ fn test_c_method_calls() { assert_eq!(2021, unique_ptr.get()); assert_eq!(old_value, unique_ptr.set2(old_value)); assert_eq!(old_value, unique_ptr.get2()); - assert_eq!(Ok(2022), unique_ptr.set_succeed(2022)); + assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap()); assert!(unique_ptr.get_fail().is_err()); } From 2de2e17bc9c94c86d4362cd2116c9f58e424dbfc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 22 2020 18:12:35 +0000 Subject: [PATCH 645/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index cc64883..c64e728 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -2,7 +2,7 @@ rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.28/src/**"]), + srcs = glob(["vendor/anyhow-1.0.31/src/**"]), visibility = ["PUBLIC"], features = ["std"], ) @@ -14,7 +14,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.52/src/**"]), + srcs = glob(["vendor/cc-1.0.54/src/**"]), visibility = ["PUBLIC"], ) @@ -31,7 +31,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.3/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.4/src/**"]), visibility = ["PUBLIC"], deps = [ ":termcolor", @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.14/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.15/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -99,7 +99,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.4/src/**"]), + srcs = glob(["vendor/quote-1.0.6/src/**"]), visibility = ["PUBLIC"], features = ["proc-macro"], deps = [":proc-macro2"], @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.20/src/**"]), + srcs = glob(["vendor/syn-1.0.23/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 313481e..abdebd6 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -7,7 +7,7 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.28/src/**"]), + srcs = glob(["vendor/anyhow-1.0.31/src/**"]), crate_features = ["std"], visibility = ["//visibility:public"], ) @@ -19,7 +19,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.52/src/**"]), + srcs = glob(["vendor/cc-1.0.54/src/**"]), visibility = ["//visibility:public"], ) @@ -36,7 +36,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.3/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.4/src/**"]), visibility = ["//visibility:public"], deps = [ ":termcolor", @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.14/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.15/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -104,7 +104,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.4/src/**"]), + srcs = glob(["vendor/quote-1.0.6/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], deps = [":proc-macro2"], @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.20/src/**"]), + srcs = glob(["vendor/syn-1.0.23/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 6e3a5b9..906d7fd 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.28" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "atty" @@ -34,9 +34,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.52" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" [[package]] name = "clap" @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5680df8512a0e825b9edc41b619ec88b367644d394b4d862a04b4d6387c65da" +checksum = "2ceface2475f2f57a7ece9ba239b96e06bda8323ee68aa6df539752f13a74f60" dependencies = [ "termcolor", "unicode-width", @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -170,9 +170,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "link-cplusplus" @@ -211,18 +211,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de40dd4ff82d9c9bab6dae29dbab1167e515f8df9ed17d2987cb6012db206933" +checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ "proc-macro2", ] @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1b5e337360b1fae433c59fcafa0c6b77c605e92540afa5221a7b81a9eca91d" +checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" dependencies = [ "proc-macro2", "quote", From 57d3c68a23833dde3fc86734fefdb5476a539fa8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 22 2020 18:13:47 +0000 Subject: [PATCH 646/2232] Release 0.3.4 --- diff --git a/Cargo.toml b/Cargo.toml index 204a1c9..6a8c1a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.3" # remember to update html_root_url +version = "0.3.4" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -19,14 +19,14 @@ default = [] # c++11 "c++17" = [] [dependencies] -cxxbridge-macro = { version = "=0.3.3", path = "macro" } +cxxbridge-macro = { version = "=0.3.4", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.3", path = "gen/build" } +cxx-build = { version = "=0.3.4", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.27", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index a5878b9..c6dab56 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.3" +version = "0.3.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 919d5c8..0609dc6 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.3" +version = "0.3.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index e4d35b8..21c4318 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.3" +version = "0.3.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index a657af7..0ad7255 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.3")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.4")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 906d7fd..d58fb5e 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.3" +version = "0.3.4" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.3" +version = "0.3.4" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.3" +version = "0.3.4" dependencies = [ "anyhow", "codespan-reporting", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.3" +version = "0.3.4" dependencies = [ "cxx", "proc-macro2", From caef2b9589bee04c2f683241c610af8efba2a8d3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 23 2020 21:58:20 +0000 Subject: [PATCH 647/2232] Remove dependency on proc-macro2/proc-macro feature --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index c6dab56..b43a37e 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,9 +13,9 @@ categories = ["development-tools::ffi"] anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.14", features = ["span-locations"] } -quote = "1.0" -syn = { version = "1.0.20", features = ["full"] } +proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } +quote = { version = "1.0", default-features = false } +syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 0609dc6..7a80a2f 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -16,10 +16,10 @@ path = "src/main.rs" [dependencies] anyhow = "1.0" codespan-reporting = "0.9" -proc-macro2 = { version = "1.0.14", features = ["span-locations"] } -quote = "1.0" +proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } +quote = { version = "1.0", default-features = false } structopt = "0.3" -syn = { version = "1.0.20", features = ["full"] } +syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/third-party/BUCK b/third-party/BUCK index c64e728..479fbfc 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.15/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.17/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", diff --git a/third-party/BUILD b/third-party/BUILD index abdebd6..8734f30 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.15/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.17/src/**"]), crate_features = [ "proc-macro", "span-locations", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index d58fb5e..0ca0a3c 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f" +checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" dependencies = [ "unicode-xid", ] From b8d211d35917184433fcab4c6ab903392d8ac551 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: May 24 2020 15:56:32 +0000 Subject: [PATCH 648/2232] Small typo fix s/when manipulation a/when manipulating a/ --- diff --git a/README.md b/README.md index 768b394..260c272 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ CXX guarantees an ABI-compatible signature that both sides understand, based on builtin bindings for key standard library types to expose an idiomatic API on those types to the other language. For example when manipulating a C++ string from Rust, its `len()` method becomes a call of the `size()` member function -defined by C++; when manipulation a Rust string from C++, its `size()` member +defined by C++; when manipulating a Rust string from C++, its `size()` member function calls Rust's `len()`.
From 63c55b6cc39ba8ae280b390f3a1ba7cff24d297c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: May 24 2020 16:02:14 +0000 Subject: [PATCH 649/2232] Merge pull request #212 from CAD97/patch-1 Small typo fix --- diff --git a/README.md b/README.md index 768b394..260c272 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ CXX guarantees an ABI-compatible signature that both sides understand, based on builtin bindings for key standard library types to expose an idiomatic API on those types to the other language. For example when manipulating a C++ string from Rust, its `len()` method becomes a call of the `size()` member function -defined by C++; when manipulation a Rust string from C++, its `size()` member +defined by C++; when manipulating a Rust string from C++, its `size()` member function calls Rust's `len()`.
From bbd2620921e921825575810a9fd40712c378d6aa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jun 14 2020 02:17:21 +0000 Subject: [PATCH 650/2232] Update ui tests to nightly-2020-06-14 --- diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index a5a0215..9ef72ca 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=std::u8::MAX }` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=u8::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A { repr: 2u8..=std::u8::MAX }` not covered + | ^ pattern `A { repr: 2u8..=u8::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` From 0c8c0f26fa0fa5d72ed7d95df976773c4c0a3e41 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 22 2020 00:57:46 +0000 Subject: [PATCH 651/2232] Unbreak ui test on c_take_callback warning --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 51716e0..84a746b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -64,7 +64,10 @@ pub mod ffi { fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); + /* + // https://github.com/dtolnay/cxx/issues/232 fn c_take_callback(callback: fn(String) -> usize); + */ fn c_take_enum(e: Enum); fn c_try_return_void() -> Result<()>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 677a55d..d70d5a0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -249,9 +249,12 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { } } +/* +// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback) { callback("2020"); } +*/ void c_take_enum(Enum e) { if (e == Enum::AVal) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index c1a08d2..7121cb2 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -69,7 +69,10 @@ void c_take_rust_vec_shared(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); +/* +// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback); +*/ void c_take_enum(Enum e); void c_try_return_void(); diff --git a/tests/test.rs b/tests/test.rs index ecfec84..f2f670c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -121,6 +121,8 @@ fn test_c_take() { check!(ffi::c_take_enum(ffi::Enum::AVal)); } +/* +// https://github.com/dtolnay/cxx/issues/232 #[test] fn test_c_callback() { fn callback(s: String) -> usize { @@ -132,6 +134,7 @@ fn test_c_callback() { check!(ffi::c_take_callback(callback)); } +*/ #[test] fn test_c_call_r() { From b3d5e600a71bce16f981887f20911dced8f3000e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 22 2020 00:58:14 +0000 Subject: [PATCH 652/2232] Update ui tests to nightly-2020-06-27 --- diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 9ef72ca..85cc0c5 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=u8::MAX }` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2_u8..=u8::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A { repr: 2u8..=u8::MAX }` not covered + | ^ pattern `A { repr: 2_u8..=u8::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` From 006723621740fee74ae1ffae522ff22523afb9b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 22 2020 01:02:36 +0000 Subject: [PATCH 653/2232] Update ui tests to nightly-2020-07-07 --- diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 5b4f6c6..cb3ae15 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` +error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` --> $DIR/wrong_type_id.rs:11:9 | 11 | type ByteRange = crate::here::StringPiece; @@ -7,7 +7,7 @@ error[E0271]: type mismatch resolving `, Id>() {} - | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | ------- required by this bound in `cxx::private::verify_extern_type` | = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` From 6911d6d604845be37fbc2f9eb66e14b553845a67 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 22 2020 01:05:48 +0000 Subject: [PATCH 654/2232] Update ui tests to nightly-2020-07-15 --- diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 0baa70a..efd1144 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -5,5 +5,4 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t | ^^^^^ doesn't have a size known at compile-time | = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` - = note: to learn more, visit = note: required because it appears within the type `TypeR` From 2b87b16679a674a8cb838b72a6ff7dec5eccf717 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 22 2020 01:26:07 +0000 Subject: [PATCH 655/2232] Merge pull request #233 from dtolnay/ui Unbreak ui tests --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 51716e0..84a746b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -64,7 +64,10 @@ pub mod ffi { fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); + /* + // https://github.com/dtolnay/cxx/issues/232 fn c_take_callback(callback: fn(String) -> usize); + */ fn c_take_enum(e: Enum); fn c_try_return_void() -> Result<()>; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 677a55d..d70d5a0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -249,9 +249,12 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { } } +/* +// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback) { callback("2020"); } +*/ void c_take_enum(Enum e) { if (e == Enum::AVal) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index c1a08d2..7121cb2 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -69,7 +69,10 @@ void c_take_rust_vec_shared(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); +/* +// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback); +*/ void c_take_enum(Enum e); void c_try_return_void(); diff --git a/tests/test.rs b/tests/test.rs index ecfec84..f2f670c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -121,6 +121,8 @@ fn test_c_take() { check!(ffi::c_take_enum(ffi::Enum::AVal)); } +/* +// https://github.com/dtolnay/cxx/issues/232 #[test] fn test_c_callback() { fn callback(s: String) -> usize { @@ -132,6 +134,7 @@ fn test_c_callback() { check!(ffi::c_take_callback(callback)); } +*/ #[test] fn test_c_call_r() { diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr index 9ef72ca..85cc0c5 100644 --- a/tests/ui/enum_match_without_wildcard.stderr +++ b/tests/ui/enum_match_without_wildcard.stderr @@ -1,11 +1,11 @@ -error[E0004]: non-exhaustive patterns: `A { repr: 2u8..=u8::MAX }` not covered +error[E0004]: non-exhaustive patterns: `A { repr: 2_u8..=u8::MAX }` not covered --> $DIR/enum_match_without_wildcard.rs:12:11 | 1 | #[cxx::bridge] | -------------- `ffi::A` defined here ... 12 | match a { - | ^ pattern `A { repr: 2u8..=u8::MAX }` not covered + | ^ pattern `A { repr: 2_u8..=u8::MAX }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `ffi::A` diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 0baa70a..efd1144 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -5,5 +5,4 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t | ^^^^^ doesn't have a size known at compile-time | = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` - = note: to learn more, visit = note: required because it appears within the type `TypeR` diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 5b4f6c6..cb3ae15 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` +error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` --> $DIR/wrong_type_id.rs:11:9 | 11 | type ByteRange = crate::here::StringPiece; @@ -7,7 +7,7 @@ error[E0271]: type mismatch resolving `, Id>() {} - | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | ------- required by this bound in `cxx::private::verify_extern_type` | = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` From 0ecd05a9bf0096d5c05a44e82d4221cc291a9ef5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 29 2020 23:32:03 +0000 Subject: [PATCH 656/2232] Include when placement new is used --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 0ed76ac..4309c9c 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -56,6 +56,7 @@ pub struct Includes { pub cstring: bool, pub exception: bool, pub memory: bool, + pub new: bool, pub string: bool, pub type_traits: bool, pub utility: bool, @@ -106,6 +107,9 @@ impl Display for Includes { if self.memory { writeln!(f, "#include ")?; } + if self.new { + writeln!(f, "#include ")?; + } if self.string { writeln!(f, "#include ")?; } diff --git a/gen/src/write.rs b/gen/src/write.rs index 2cf9faf..46b6151 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -149,11 +149,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { for ty in types { match ty { Type::RustBox(_) => { + out.include.new = true; out.include.type_traits = true; needs_rust_box = true; } Type::RustVec(_) => { out.include.array = true; + out.include.new = true; out.include.type_traits = true; needs_rust_vec = true; } @@ -463,6 +465,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); } if indirect_return { + out.include.new = true; write!(out, "new (return$) "); write_indirect_return_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); @@ -1149,6 +1152,7 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { // Shared by UniquePtr and UniquePtr>. fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.new = true; out.include.utility = true; let inner = to_typename(&out.namespace, ty); let instance = to_mangled(&out.namespace, ty); diff --git a/include/cxx.h b/include/cxx.h index 9f4fb47..f211b3b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From 4caede68764f2ed0a57fba26f10c3728fc1849b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 29 2020 23:37:28 +0000 Subject: [PATCH 657/2232] Merge pull request #239 from dtolnay/new Include when placement new is used --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 0ed76ac..4309c9c 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -56,6 +56,7 @@ pub struct Includes { pub cstring: bool, pub exception: bool, pub memory: bool, + pub new: bool, pub string: bool, pub type_traits: bool, pub utility: bool, @@ -106,6 +107,9 @@ impl Display for Includes { if self.memory { writeln!(f, "#include ")?; } + if self.new { + writeln!(f, "#include ")?; + } if self.string { writeln!(f, "#include ")?; } diff --git a/gen/src/write.rs b/gen/src/write.rs index 2cf9faf..46b6151 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -149,11 +149,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { for ty in types { match ty { Type::RustBox(_) => { + out.include.new = true; out.include.type_traits = true; needs_rust_box = true; } Type::RustVec(_) => { out.include.array = true; + out.include.new = true; out.include.type_traits = true; needs_rust_vec = true; } @@ -463,6 +465,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { write!(out, " "); } if indirect_return { + out.include.new = true; write!(out, "new (return$) "); write_indirect_return_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); @@ -1149,6 +1152,7 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { // Shared by UniquePtr and UniquePtr>. fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { + out.include.new = true; out.include.utility = true; let inner = to_typename(&out.namespace, ty); let instance = to_mangled(&out.namespace, ty); diff --git a/include/cxx.h b/include/cxx.h index 9f4fb47..f211b3b 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From 21f0ff042445cf6f0574a583231b5c68e50263f5 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Jul 30 2020 00:31:16 +0000 Subject: [PATCH 658/2232] Allow DSO export for C Rust bindings. This option to the 'cxxbridge' command line tool allows users to specify (for example) __attribute__((visibility("default"))) or __declspec(dllexport) on C functions which may be exported from a shared object as they may be required by Rust code in different binaries. --- diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index a20179f..701f77f 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -35,6 +35,15 @@ struct Opt { #[structopt(long)] header: bool, + /// Optional annotation for implementations of C++ function + /// wrappers that may be exposed to Rust. You may for example + /// need to provide __declspec(dllexport) or + /// __attribute__((visibility("default"))) if Rust code from + /// one shared object or executable depends on these C++ functions + /// in another. + #[structopt(long)] + cxx_impl_annotations: Option, + /// Any additional headers to #include #[structopt(short, long)] include: Vec, @@ -49,6 +58,7 @@ fn main() { let gen = gen::Opt { include: opt.include, + cxx_impl_annotations: opt.cxx_impl_annotations, }; match (opt.input, opt.header) { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index b9d35d7..65d6c67 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -24,6 +24,9 @@ struct Input { pub(super) struct Opt { /// Any additional headers to #include pub include: Vec, + /// Whether to set __attribute__((visibility("default"))) + /// or similar annotations on function implementations. + pub cxx_impl_annotations: Option, } pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { diff --git a/gen/src/write.rs b/gen/src/write.rs index 46b6151..04d8e3e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -86,13 +86,13 @@ pub(super) fn gen( out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { - let (efn, write): (_, fn(_, _, _)) = match api { + let (efn, write): (_, fn(_, _, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), Api::RustFunction(efn) => (efn, write_rust_function_decl), _ => continue, }; out.next_section(); - write(out, efn, types); + write(out, efn, types, &opt.cxx_impl_annotations); } out.end_block("extern \"C\""); } @@ -399,7 +399,17 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { } } -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { +fn write_cxx_function_shim( + out: &mut OutFile, + efn: &ExternFn, + types: &Types, + impl_annotations: &Option, +) { + if !out.header { + if let Some(annotation) = impl_annotations { + write!(out, "{} ", annotation); + } + } if efn.throws { write!(out, "::rust::Str::Repr "); } else { @@ -560,7 +570,7 @@ fn write_function_pointer_trampoline( write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { let link_name = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); From 8205e625c11e432f892439a82dfd80950c8ec798 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Jul 30 2020 00:31:16 +0000 Subject: [PATCH 659/2232] Adding test for new annotations. --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 65d6c67..77a0c65 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -31,34 +31,74 @@ pub(super) struct Opt { pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; - generate(path, opt, header) + generate_from_path(path, opt, header) } pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { let header = true; - generate(path, opt, header) + generate_from_path(path, opt, header) } -fn generate(path: &Path, opt: Opt, header: bool) -> Vec { +fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match (|| -> Result<_> { - proc_macro2::fallback::force(); - let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; - let bridge = find::find_bridge_mod(syntax)?; - let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.module); - let ref types = Types::collect(errors, apis); - errors.propagate()?; - check::typecheck(errors, namespace, apis, types); - errors.propagate()?; - let out = write::gen(namespace, apis, types, opt, header); - Ok(out) - })() { - Ok(out) => out.content(), + match generate(&source, opt, header) { + Ok(out) => out, Err(err) => format_err(path, &source, err), } } + +fn generate(source: &str, opt: Opt, header: bool) -> Result> { + proc_macro2::fallback::force(); + let ref mut errors = Errors::new(); + let syntax = syn::parse_file(&source)?; + let bridge = find::find_bridge_mod(syntax)?; + let ref namespace = bridge.namespace; + let ref apis = syntax::parse_items(errors, bridge.module); + let ref types = Types::collect(errors, apis); + errors.propagate()?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; + let out = write::gen(namespace, apis, types, opt, header); + Ok(out.content()) +} + +#[cfg(test)] +mod tests { + use crate::gen::{generate, Opt}; + + const CPP_EXAMPLE: &'static str = r#" + #[cxx::bridge] + mod ffi { + extern "C" { + pub fn do_cpp_thing(foo: &str); + } + } + "#; + + #[test] + fn test_cpp() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: None, + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + // To avoid continual breakage we won't test every byte. + // Let's look for the major features. + assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + } + + #[test] + fn test_annotation() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: Some("ANNOTATION".to_string()), + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + } +} From b1dced5711b32a12c7b78aab31cd95d18e5580d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 30 2020 00:37:56 +0000 Subject: [PATCH 660/2232] Merge pull request #231 from adetaylor/set-visibility Allow DSO export for C Rust bindings. --- diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index a20179f..701f77f 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -35,6 +35,15 @@ struct Opt { #[structopt(long)] header: bool, + /// Optional annotation for implementations of C++ function + /// wrappers that may be exposed to Rust. You may for example + /// need to provide __declspec(dllexport) or + /// __attribute__((visibility("default"))) if Rust code from + /// one shared object or executable depends on these C++ functions + /// in another. + #[structopt(long)] + cxx_impl_annotations: Option, + /// Any additional headers to #include #[structopt(short, long)] include: Vec, @@ -49,6 +58,7 @@ fn main() { let gen = gen::Opt { include: opt.include, + cxx_impl_annotations: opt.cxx_impl_annotations, }; match (opt.input, opt.header) { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index b9d35d7..77a0c65 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -24,38 +24,81 @@ struct Input { pub(super) struct Opt { /// Any additional headers to #include pub include: Vec, + /// Whether to set __attribute__((visibility("default"))) + /// or similar annotations on function implementations. + pub cxx_impl_annotations: Option, } pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; - generate(path, opt, header) + generate_from_path(path, opt, header) } pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { let header = true; - generate(path, opt, header) + generate_from_path(path, opt, header) } -fn generate(path: &Path, opt: Opt, header: bool) -> Vec { +fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match (|| -> Result<_> { - proc_macro2::fallback::force(); - let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; - let bridge = find::find_bridge_mod(syntax)?; - let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.module); - let ref types = Types::collect(errors, apis); - errors.propagate()?; - check::typecheck(errors, namespace, apis, types); - errors.propagate()?; - let out = write::gen(namespace, apis, types, opt, header); - Ok(out) - })() { - Ok(out) => out.content(), + match generate(&source, opt, header) { + Ok(out) => out, Err(err) => format_err(path, &source, err), } } + +fn generate(source: &str, opt: Opt, header: bool) -> Result> { + proc_macro2::fallback::force(); + let ref mut errors = Errors::new(); + let syntax = syn::parse_file(&source)?; + let bridge = find::find_bridge_mod(syntax)?; + let ref namespace = bridge.namespace; + let ref apis = syntax::parse_items(errors, bridge.module); + let ref types = Types::collect(errors, apis); + errors.propagate()?; + check::typecheck(errors, namespace, apis, types); + errors.propagate()?; + let out = write::gen(namespace, apis, types, opt, header); + Ok(out.content()) +} + +#[cfg(test)] +mod tests { + use crate::gen::{generate, Opt}; + + const CPP_EXAMPLE: &'static str = r#" + #[cxx::bridge] + mod ffi { + extern "C" { + pub fn do_cpp_thing(foo: &str); + } + } + "#; + + #[test] + fn test_cpp() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: None, + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + // To avoid continual breakage we won't test every byte. + // Let's look for the major features. + assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + } + + #[test] + fn test_annotation() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: Some("ANNOTATION".to_string()), + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + } +} diff --git a/gen/src/write.rs b/gen/src/write.rs index 46b6151..04d8e3e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -86,13 +86,13 @@ pub(super) fn gen( out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { - let (efn, write): (_, fn(_, _, _)) = match api { + let (efn, write): (_, fn(_, _, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), Api::RustFunction(efn) => (efn, write_rust_function_decl), _ => continue, }; out.next_section(); - write(out, efn, types); + write(out, efn, types, &opt.cxx_impl_annotations); } out.end_block("extern \"C\""); } @@ -399,7 +399,17 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { } } -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { +fn write_cxx_function_shim( + out: &mut OutFile, + efn: &ExternFn, + types: &Types, + impl_annotations: &Option, +) { + if !out.header { + if let Some(annotation) = impl_annotations { + write!(out, "{} ", annotation); + } + } if efn.throws { write!(out, "::rust::Str::Repr "); } else { @@ -560,7 +570,7 @@ fn write_function_pointer_trampoline( write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) { +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { let link_name = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); From c2bbd952d2dee6005562d309e10855b177610463 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 30 2020 01:17:19 +0000 Subject: [PATCH 661/2232] Add rust::String(const char *, size_t) constructor --- diff --git a/include/cxx.h b/include/cxx.h index f211b3b..5a33a7a 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -29,6 +29,7 @@ public: String(const std::string &); String(const char *); + String(const char *, size_t); String &operator=(const String &) noexcept; String &operator=(String &&) noexcept; diff --git a/src/cxx.cc b/src/cxx.cc index eb86d3d..8614d39 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -55,16 +55,11 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge03$string$drop(this); } -String::String(const std::string &s) { - auto ptr = s.data(); - auto len = s.length(); - if (!cxxbridge03$string$from(this, ptr, len)) { - panic("data for rust::String is not utf-8"); - } -} +String::String(const std::string &s) : String(s.data(), s.length()) {} + +String::String(const char *s) : String(s, std::strlen(s)) {} -String::String(const char *s) { - auto len = std::strlen(s); +String::String(const char *s, size_t len) { if (!cxxbridge03$string$from(this, s, len)) { panic("data for rust::String is not utf-8"); } From 894c5e45dcaa7b4eac7dce16bfcd86819f185e79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 30 2020 01:21:50 +0000 Subject: [PATCH 662/2232] Add rust::Str(const char *, size_t) constructor --- diff --git a/include/cxx.h b/include/cxx.h index 5a33a7a..c3231fd 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -59,6 +59,7 @@ public: Str(const std::string &); Str(const char *); + Str(const char *, size_t); Str(std::string &&) = delete; Str &operator=(Str) noexcept; diff --git a/src/cxx.cc b/src/cxx.cc index 8614d39..da05ee2 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -106,13 +106,11 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } -} +Str::Str(const std::string &s) : Str(s.data(), s.length()) {} + +Str::Str(const char *s) : Str(s, std::strlen(s)) {} -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { +Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } From 28da250d243949be488964ffffeb1c4962285611 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 30 2020 01:41:43 +0000 Subject: [PATCH 663/2232] Merge pull request #242 from dtolnay/str-ptr-len Add rust::String(const char *, size_t) constructor --- diff --git a/include/cxx.h b/include/cxx.h index f211b3b..c3231fd 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -29,6 +29,7 @@ public: String(const std::string &); String(const char *); + String(const char *, size_t); String &operator=(const String &) noexcept; String &operator=(String &&) noexcept; @@ -58,6 +59,7 @@ public: Str(const std::string &); Str(const char *); + Str(const char *, size_t); Str(std::string &&) = delete; Str &operator=(Str) noexcept; diff --git a/src/cxx.cc b/src/cxx.cc index eb86d3d..da05ee2 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -55,16 +55,11 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge03$string$drop(this); } -String::String(const std::string &s) { - auto ptr = s.data(); - auto len = s.length(); - if (!cxxbridge03$string$from(this, ptr, len)) { - panic("data for rust::String is not utf-8"); - } -} +String::String(const std::string &s) : String(s.data(), s.length()) {} -String::String(const char *s) { - auto len = std::strlen(s); +String::String(const char *s) : String(s, std::strlen(s)) {} + +String::String(const char *s, size_t len) { if (!cxxbridge03$string$from(this, s, len)) { panic("data for rust::String is not utf-8"); } @@ -111,13 +106,11 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } -} +Str::Str(const std::string &s) : Str(s.data(), s.length()) {} + +Str::Str(const char *s) : Str(s, std::strlen(s)) {} -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { +Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } From 0d47a535769d3511e6e5ecb978e739abab8b7a77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 02:39:04 +0000 Subject: [PATCH 664/2232] Move C++ code generator unit tests to module --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 77a0c65..710bca2 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -7,6 +7,9 @@ pub(super) mod include; pub(super) mod out; mod write; +#[cfg(test)] +mod tests; + use self::error::{format_err, Error, Result}; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; @@ -64,41 +67,3 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { let out = write::gen(namespace, apis, types, opt, header); Ok(out.content()) } - -#[cfg(test)] -mod tests { - use crate::gen::{generate, Opt}; - - const CPP_EXAMPLE: &'static str = r#" - #[cxx::bridge] - mod ffi { - extern "C" { - pub fn do_cpp_thing(foo: &str); - } - } - "#; - - #[test] - fn test_cpp() { - let opts = Opt { - include: Vec::new(), - cxx_impl_annotations: None, - }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); - let output = std::str::from_utf8(&output).unwrap(); - // To avoid continual breakage we won't test every byte. - // Let's look for the major features. - assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); - } - - #[test] - fn test_annotation() { - let opts = Opt { - include: Vec::new(), - cxx_impl_annotations: Some("ANNOTATION".to_string()), - }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); - let output = std::str::from_utf8(&output).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); - } -} diff --git a/gen/src/tests.rs b/gen/src/tests.rs new file mode 100644 index 0000000..0e7a910 --- /dev/null +++ b/gen/src/tests.rs @@ -0,0 +1,34 @@ +use crate::gen::{generate, Opt}; + +const CPP_EXAMPLE: &'static str = r#" + #[cxx::bridge] + mod ffi { + extern "C" { + pub fn do_cpp_thing(foo: &str); + } + } + "#; + +#[test] +fn test_cpp() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: None, + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + // To avoid continual breakage we won't test every byte. + // Let's look for the major features. + assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); +} + +#[test] +fn test_annotation() { + let opts = Opt { + include: Vec::new(), + cxx_impl_annotations: Some("ANNOTATION".to_string()), + }; + let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = std::str::from_utf8(&output).unwrap(); + assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); +} From 829b0707e6c44d0c9b2286bd2b48787f5d87e16e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:24:22 +0000 Subject: [PATCH 665/2232] Update bazel build to rust 1.44 --- diff --git a/WORKSPACE b/WORKSPACE index 4269fbc..cafb78c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,13 +24,13 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_43_linux", + name = "rust_1_44_linux", exec_triple = "x86_64-unknown-linux-gnu", - version = "1.43.0", + version = "1.44.0", ) rust_repository_set( - name = "rust_1_43_darwin", + name = "rust_1_44_darwin", exec_triple = "x86_64-apple-darwin", - version = "1.43.0", + version = "1.44.0", ) From a96213c6006665ed14adbe9a9b8f69226ae34b5f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:24:22 +0000 Subject: [PATCH 666/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 479fbfc..9890a71 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -2,7 +2,7 @@ rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.31/src/**"]), + srcs = glob(["vendor/anyhow-1.0.32/src/**"]), visibility = ["PUBLIC"], features = ["std"], ) @@ -14,7 +14,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.54/src/**"]), + srcs = glob(["vendor/cc-1.0.58/src/**"]), visibility = ["PUBLIC"], ) @@ -31,7 +31,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.4/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]), visibility = ["PUBLIC"], deps = [ ":termcolor", @@ -53,13 +53,13 @@ rust_library( rust_library( name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), visibility = ["PUBLIC"], ) rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.2/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -71,7 +71,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.2/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), proc_macro = True, deps = [ ":proc-macro2", @@ -83,7 +83,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.17/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -99,7 +99,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.6/src/**"]), + srcs = glob(["vendor/quote-1.0.7/src/**"]), visibility = ["PUBLIC"], features = ["proc-macro"], deps = [":proc-macro2"], @@ -107,7 +107,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.14/src/**"]), + srcs = glob(["vendor/structopt-0.3.15/src/**"]), visibility = ["PUBLIC"], deps = [ ":clap", @@ -118,7 +118,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.7/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), proc_macro = True, deps = [ ":heck", @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.23/src/**"]), + srcs = glob(["vendor/syn-1.0.36/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", @@ -177,10 +177,10 @@ rust_library( rust_library( name = "unicode-width", - srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), + srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) rust_library( name = "unicode-xid", - srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), + srcs = glob(["vendor/unicode-xid-0.2.1/src/**"]), ) diff --git a/third-party/BUILD b/third-party/BUILD index 8734f30..9aaf4f6 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -7,7 +7,7 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") rust_library( name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.31/src/**"]), + srcs = glob(["vendor/anyhow-1.0.32/src/**"]), crate_features = ["std"], visibility = ["//visibility:public"], ) @@ -19,7 +19,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.54/src/**"]), + srcs = glob(["vendor/cc-1.0.58/src/**"]), visibility = ["//visibility:public"], ) @@ -36,7 +36,7 @@ rust_library( rust_library( name = "codespan-reporting", - srcs = glob(["vendor/codespan-reporting-0.9.4/src/**"]), + srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]), visibility = ["//visibility:public"], deps = [ ":termcolor", @@ -58,13 +58,13 @@ rust_library( rust_library( name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.1/src/**"]), + srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), visibility = ["//visibility:public"], ) rust_library( name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.2/src/**"]), + srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), rustc_flags = ["--cfg=use_fallback"], deps = [ ":proc-macro-error-attr", @@ -76,7 +76,7 @@ rust_library( rust_library( name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.2/src/**"]), + srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), crate_type = "proc-macro", deps = [ ":proc-macro2", @@ -88,7 +88,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.17/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -104,7 +104,7 @@ rust_library( rust_library( name = "quote", - srcs = glob(["vendor/quote-1.0.6/src/**"]), + srcs = glob(["vendor/quote-1.0.7/src/**"]), crate_features = ["proc-macro"], visibility = ["//visibility:public"], deps = [":proc-macro2"], @@ -112,7 +112,7 @@ rust_library( rust_library( name = "structopt", - srcs = glob(["vendor/structopt-0.3.14/src/**"]), + srcs = glob(["vendor/structopt-0.3.15/src/**"]), visibility = ["//visibility:public"], deps = [ ":clap", @@ -123,7 +123,7 @@ rust_library( rust_library( name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.7/src/**"]), + srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), crate_type = "proc-macro", deps = [ ":heck", @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.23/src/**"]), + srcs = glob(["vendor/syn-1.0.36/src/**"]), crate_features = [ "clone-impls", "derive", @@ -182,10 +182,10 @@ rust_library( rust_library( name = "unicode-width", - srcs = glob(["vendor/unicode-width-0.1.7/src/**"]), + srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) rust_library( name = "unicode-xid", - srcs = glob(["vendor/unicode-xid-0.2.0/src/**"]), + srcs = glob(["vendor/unicode-xid-0.2.1/src/**"]), ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 0ca0a3c..8f40664 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "atty" @@ -34,9 +34,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.54" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" [[package]] name = "clap" @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ceface2475f2f57a7ece9ba239b96e06bda8323ee68aa6df539752f13a74f60" +checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d" dependencies = [ "termcolor", "unicode-width", @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "dissimilar" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39de161cd2ebbd6e5783db53a82a47b6a47dcfef754130839603561745528b94" +checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb" [[package]] name = "glob" @@ -149,18 +149,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" dependencies = [ "libc", ] [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "lazy_static" @@ -170,24 +170,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.70" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" [[package]] name = "link-cplusplus" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628cd9d7b5c99cb930617438a3d7896f5eb734647bc2838ded9ca50689507295" +checksum = "f563b3814ea63e830e3e321206e4e2b5177854586ebe3f595795ed7053b217f5" dependencies = [ "cc", ] [[package]] name = "proc-macro-error" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" +checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" +checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" dependencies = [ "proc-macro2", "quote", @@ -211,27 +211,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] [[package]] name = "rustversion" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +checksum = "b9bdc5e856e51e685846fb6c13a1f5e5432946c2c90501bdc76a1319f19e29da" dependencies = [ "proc-macro2", "quote", @@ -240,24 +240,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.110" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.110" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" dependencies = [ "proc-macro2", "quote", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.53" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ "itoa", "ryu", @@ -283,9 +283,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" dependencies = [ "clap", "lazy_static", @@ -294,9 +294,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" dependencies = [ "heck", "proc-macro-error", @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.23" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269" +checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" dependencies = [ "proc-macro2", "quote", @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.27" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744665442556a91933cee5e75b0371376eb03498c4d0bfbcebd2a9882b4fb5ef" +checksum = "7a4d94e6adf00b96b1ab94fcfcd8c3cf916733b39adf90c8f72693629887b9b8" dependencies = [ "dissimilar", "glob", @@ -377,15 +377,15 @@ checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vec_map" @@ -395,15 +395,15 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", From dd24719b97c1d26d1db9add7668e1fec21f9eb5e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:30:02 +0000 Subject: [PATCH 667/2232] Update bazel Rust rules --- diff --git a/BUILD b/BUILD index d63fea7..4d2b33a 100644 --- a/BUILD +++ b/BUILD @@ -3,10 +3,12 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), + proc_macro_deps = [ + ":cxxbridge-macro", + ], visibility = ["//visibility:public"], deps = [ ":core-lib", - ":cxxbridge-macro", "//third-party:link-cplusplus", ], ) diff --git a/WORKSPACE b/WORKSPACE index cafb78c..cd2a2e8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "b83154a58f95618e06845b774b079000e0c39830e185db4c7bf46e79896cb3a1", - strip_prefix = "rules_rust-0deef6dd8180cd3bc610878558bb26921b4e8de1", - # Master branch as of 2020-03-07 - url = "https://github.com/bazelbuild/rules_rust/archive/0deef6dd8180cd3bc610878558bb26921b4e8de1.tar.gz", + sha256 = "5ed804fcd10a506a5b8e9e59bc6b3b7f43bc30c87ce4670e6f78df43604894fd", + strip_prefix = "rules_rust-fdf9655ba95616e0314b4e0ebab40bb0c5fe005c", + # Master branch as of 2020-07-30 + url = "https://github.com/bazelbuild/rules_rust/archive/fdf9655ba95616e0314b4e0ebab40bb0c5fe005c.tar.gz", ) http_archive( diff --git a/third-party/BUILD b/third-party/BUILD index 9aaf4f6..a01d4b7 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -65,9 +65,11 @@ rust_library( rust_library( name = "proc-macro-error", srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), + proc_macro_deps = [ + ":proc-macro-error-attr", + ], rustc_flags = ["--cfg=use_fallback"], deps = [ - ":proc-macro-error-attr", ":proc-macro2", ":quote", ":syn", @@ -113,11 +115,13 @@ rust_library( rust_library( name = "structopt", srcs = glob(["vendor/structopt-0.3.15/src/**"]), + proc_macro_deps = [ + ":structopt-derive", + ], visibility = ["//visibility:public"], deps = [ ":clap", ":lazy_static", - ":structopt-derive", ], ) From bf0e48bdb0bc10aeb71f005a1239db6ea8dc106d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:41:39 +0000 Subject: [PATCH 668/2232] Merge pull request #244 from dtolnay/bazel Update bazel Rust rules repo --- diff --git a/BUILD b/BUILD index d63fea7..4d2b33a 100644 --- a/BUILD +++ b/BUILD @@ -3,10 +3,12 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), + proc_macro_deps = [ + ":cxxbridge-macro", + ], visibility = ["//visibility:public"], deps = [ ":core-lib", - ":cxxbridge-macro", "//third-party:link-cplusplus", ], ) diff --git a/WORKSPACE b/WORKSPACE index cafb78c..cd2a2e8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,10 +2,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_rust", - sha256 = "b83154a58f95618e06845b774b079000e0c39830e185db4c7bf46e79896cb3a1", - strip_prefix = "rules_rust-0deef6dd8180cd3bc610878558bb26921b4e8de1", - # Master branch as of 2020-03-07 - url = "https://github.com/bazelbuild/rules_rust/archive/0deef6dd8180cd3bc610878558bb26921b4e8de1.tar.gz", + sha256 = "5ed804fcd10a506a5b8e9e59bc6b3b7f43bc30c87ce4670e6f78df43604894fd", + strip_prefix = "rules_rust-fdf9655ba95616e0314b4e0ebab40bb0c5fe005c", + # Master branch as of 2020-07-30 + url = "https://github.com/bazelbuild/rules_rust/archive/fdf9655ba95616e0314b4e0ebab40bb0c5fe005c.tar.gz", ) http_archive( diff --git a/third-party/BUILD b/third-party/BUILD index 9aaf4f6..a01d4b7 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -65,9 +65,11 @@ rust_library( rust_library( name = "proc-macro-error", srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), + proc_macro_deps = [ + ":proc-macro-error-attr", + ], rustc_flags = ["--cfg=use_fallback"], deps = [ - ":proc-macro-error-attr", ":proc-macro2", ":quote", ":syn", @@ -113,11 +115,13 @@ rust_library( rust_library( name = "structopt", srcs = glob(["vendor/structopt-0.3.15/src/**"]), + proc_macro_deps = [ + ":structopt-derive", + ], visibility = ["//visibility:public"], deps = [ ":clap", ":lazy_static", - ":structopt-derive", ], ) From 39fa366897a7d8fe9bfedfff0d7d45aebe1e7f1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:41:52 +0000 Subject: [PATCH 669/2232] Switch cli arg parsing from structopt to clap --- diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 7a80a2f..b9d4b78 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -15,10 +15,10 @@ path = "src/main.rs" [dependencies] anyhow = "1.0" +clap = "2.33" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } -structopt = "0.3" syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs new file mode 100644 index 0000000..37934e5 --- /dev/null +++ b/gen/cmd/src/app.rs @@ -0,0 +1,112 @@ +use super::Opt; +use clap::AppSettings; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +type App = clap::App<'static, 'static>; +type Arg = clap::Arg<'static, 'static>; + +const USAGE: &str = "\ + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit rust/cxx.h header to stdout\ +"; + +const TEMPLATE: &str = "\ +{bin} {version} +David Tolnay +https://github.com/dtolnay/cxx + +USAGE: + {usage} + +ARGS: +{positionals} +OPTIONS: +{unified}\ +"; + +fn app() -> App { + let mut app = App::new("cxxbridge") + .usage(USAGE) + .template(TEMPLATE) + .setting(AppSettings::NextLineHelp) + .arg(arg_input()) + .arg(arg_cxx_impl_annotations()) + .arg(arg_header()) + .arg(arg_include()) + .help_message("Print help information.") + .version_message("Print version information."); + if let Some(version) = option_env!("CARGO_PKG_VERSION") { + app = app.version(version); + } + app +} + +const INPUT: &str = "input"; +const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; +const HEADER: &str = "header"; +const INCLUDE: &str = "include"; + +pub(super) fn from_args() -> Opt { + let matches = app().get_matches(); + Opt { + input: matches.value_of_os(INPUT).map(PathBuf::from), + cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned), + header: matches.is_present(HEADER), + include: matches + .values_of(INCLUDE) + .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()), + } +} + +fn validate_utf8(arg: &OsStr) -> Result<(), OsString> { + if arg.to_str().is_some() { + Ok(()) + } else { + Err(OsString::from("invalid utf-8 sequence")) + } +} + +fn arg_input() -> Arg { + Arg::with_name(INPUT) + .help("Input Rust source file containing #[cxx::bridge].") + .required_unless(HEADER) +} + +fn arg_cxx_impl_annotations() -> Arg { + const HELP: &str = "\ +Optional annotation for implementations of C++ function wrappers +that may be exposed to Rust. You may for example need to provide +__declspec(dllexport) or __attribute__((visibility(\"default\"))) +if Rust code from one shared object or executable depends on +these C++ functions in another. + "; + Arg::with_name(CXX_IMPL_ANNOTATIONS) + .long(CXX_IMPL_ANNOTATIONS) + .takes_value(true) + .value_name("annotation") + .validator_os(validate_utf8) + .help(HELP) +} + +fn arg_header() -> Arg { + Arg::with_name(HEADER) + .long(HEADER) + .help("Emit header with declarations only.") +} + +fn arg_include() -> Arg { + const HELP: &str = "\ +Any additional headers to #include. The cxxbridge tool does not +parse or even require the given paths to exist; they simply go +into the generated C++ code as #include lines. + "; + Arg::with_name(INCLUDE) + .long(INCLUDE) + .short("i") + .takes_value(true) + .multiple(true) + .validator_os(validate_utf8) + .help(HELP) +} diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 701f77f..0160913 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -6,46 +6,19 @@ clippy::toplevel_ref_arg )] +mod app; mod gen; mod syntax; use gen::include; use std::io::{self, Write}; use std::path::PathBuf; -use structopt::StructOpt; -#[derive(StructOpt, Debug)] -#[structopt( - name = "cxxbridge", - author = "David Tolnay ", - about = "https://github.com/dtolnay/cxx", - usage = "\ - cxxbridge .rs Emit .cc file for bridge to stdout - cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout", - help_message = "Print help information", - version_message = "Print version information" -)] +#[derive(Debug)] struct Opt { - /// Input Rust source file containing #[cxx::bridge] - #[structopt(parse(from_os_str), required_unless = "header")] input: Option, - - /// Emit header with declarations only - #[structopt(long)] header: bool, - - /// Optional annotation for implementations of C++ function - /// wrappers that may be exposed to Rust. You may for example - /// need to provide __declspec(dllexport) or - /// __attribute__((visibility("default"))) if Rust code from - /// one shared object or executable depends on these C++ functions - /// in another. - #[structopt(long)] cxx_impl_annotations: Option, - - /// Any additional headers to #include - #[structopt(short, long)] include: Vec, } @@ -54,7 +27,7 @@ fn write(content: impl AsRef<[u8]>) { } fn main() { - let opt = Opt::from_args(); + let opt = app::from_args(); let gen = gen::Opt { include: opt.include, From b316d0bb1c34a8421579fc0301fbdc9dfb1b1d33 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 05:42:34 +0000 Subject: [PATCH 670/2232] Remove structopt from third-party --- diff --git a/BUCK b/BUCK index 241d7b3..c554e1f 100644 --- a/BUCK +++ b/BUCK @@ -17,10 +17,10 @@ rust_binary( visibility = ["PUBLIC"], deps = [ "//third-party:anyhow", + "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", - "//third-party:structopt", "//third-party:syn", ], ) diff --git a/BUILD b/BUILD index 4d2b33a..d5255a8 100644 --- a/BUILD +++ b/BUILD @@ -20,10 +20,10 @@ rust_binary( visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", + "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", - "//third-party:structopt", "//third-party:syn", ], ) diff --git a/third-party/BUCK b/third-party/BUCK index 9890a71..78fb2a9 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -22,6 +22,7 @@ rust_library( name = "clap", srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", + visibility = ["PUBLIC"], deps = [ ":bitflags", ":textwrap", @@ -40,13 +41,6 @@ rust_library( ) rust_library( - name = "heck", - srcs = glob(["vendor/heck-0.3.1/src/**"]), - edition = "2015", - deps = [":unicode-segmentation"], -) - -rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), ) @@ -58,30 +52,6 @@ rust_library( ) rust_library( - name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), - rustc_flags = ["--cfg=use_fallback"], - deps = [ - ":proc-macro-error-attr", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( - name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), - proc_macro = True, - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ":syn-mid", - ], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), visibility = ["PUBLIC"], @@ -106,30 +76,6 @@ rust_library( ) rust_library( - name = "structopt", - srcs = glob(["vendor/structopt-0.3.15/src/**"]), - visibility = ["PUBLIC"], - deps = [ - ":clap", - ":lazy_static", - ":structopt-derive", - ], -) - -rust_library( - name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), - proc_macro = True, - deps = [ - ":heck", - ":proc-macro-error", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.36/src/**"]), visibility = ["PUBLIC"], @@ -149,16 +95,6 @@ rust_library( ) rust_library( - name = "syn-mid", - srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "termcolor", srcs = glob(["vendor/termcolor-1.1.0/src/**"]), ) @@ -170,12 +106,6 @@ rust_library( ) rust_library( - name = "unicode-segmentation", - srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), - edition = "2015", -) - -rust_library( name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) diff --git a/third-party/BUILD b/third-party/BUILD index a01d4b7..3e0e30b 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -27,6 +27,7 @@ rust_library( name = "clap", srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", + visibility = ["//visibility:public"], deps = [ ":bitflags", ":textwrap", @@ -45,13 +46,6 @@ rust_library( ) rust_library( - name = "heck", - srcs = glob(["vendor/heck-0.3.1/src/**"]), - edition = "2015", - deps = [":unicode-segmentation"], -) - -rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), ) @@ -63,32 +57,6 @@ rust_library( ) rust_library( - name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), - proc_macro_deps = [ - ":proc-macro-error-attr", - ], - rustc_flags = ["--cfg=use_fallback"], - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( - name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), - crate_type = "proc-macro", - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ":syn-mid", - ], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), crate_features = [ @@ -113,32 +81,6 @@ rust_library( ) rust_library( - name = "structopt", - srcs = glob(["vendor/structopt-0.3.15/src/**"]), - proc_macro_deps = [ - ":structopt-derive", - ], - visibility = ["//visibility:public"], - deps = [ - ":clap", - ":lazy_static", - ], -) - -rust_library( - name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), - crate_type = "proc-macro", - deps = [ - ":heck", - ":proc-macro-error", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.36/src/**"]), crate_features = [ @@ -158,16 +100,6 @@ rust_library( ) rust_library( - name = "syn-mid", - srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "termcolor", srcs = glob(["vendor/termcolor-1.1.0/src/**"]), ) @@ -179,12 +111,6 @@ rust_library( ) rust_library( - name = "unicode-segmentation", - srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), - edition = "2015", -) - -rust_library( name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 8f40664..521c030 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -101,10 +101,10 @@ name = "cxxbridge-cmd" version = "0.3.4" dependencies = [ "anyhow", + "clap", "codespan-reporting", "proc-macro2", "quote", - "structopt", "syn", ] @@ -139,15 +139,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - -[[package]] name = "hermit-abi" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -184,32 +175,6 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syn-mid", - "version_check", -] - -[[package]] name = "proc-macro2" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -282,30 +247,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] -name = "structopt" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "syn" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -317,17 +258,6 @@ dependencies = [ ] [[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -370,12 +300,6 @@ dependencies = [ ] [[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -394,12 +318,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" From f4632ded18bd464bbd23e00206afd1151f5803b8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 17:46:55 +0000 Subject: [PATCH 671/2232] #pragma once needs to be above includes --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 4309c9c..54a359f 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -127,9 +127,6 @@ impl Display for Includes { writeln!(f, "#include ")?; writeln!(f, "#endif")?; } - if *self != Self::default() { - writeln!(f)?; - } Ok(()) } } diff --git a/gen/src/out.rs b/gen/src/out.rs index 08bf85f..f07a0d3 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -51,16 +51,15 @@ impl OutFile { } } - pub fn prepend(&mut self, section: String) { - let content = self.content.get_mut(); - content.bytes.splice(..0, section.into_bytes()); - } - pub fn write_fmt(&self, args: Arguments) { let content = &mut *self.content.borrow_mut(); Write::write_fmt(content, args).unwrap(); } + pub fn extend(&self, other: &Self) { + self.content.borrow_mut().write_bytes(&other.content.borrow().bytes); + } + pub fn content(&self) -> Vec { self.content.borrow().bytes.clone() } @@ -68,7 +67,14 @@ impl OutFile { impl Write for Content { fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { + self.write_bytes(s.as_bytes()); + Ok(()) + } +} + +impl Content { + fn write_bytes(&mut self, b: &[u8]) { + if !b.is_empty() { if !self.blocks_pending.is_empty() { if !self.bytes.is_empty() { self.bytes.push(b'\n'); @@ -84,8 +90,7 @@ impl Write for Content { } self.section_pending = false; } - self.bytes.extend_from_slice(s.as_bytes()); + self.bytes.extend_from_slice(b); } - Ok(()) } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 04d8e3e..88a0734 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -17,10 +17,6 @@ pub(super) fn gen( let mut out_file = OutFile::new(namespace.clone(), header); let out = &mut out_file; - if header { - writeln!(out, "#pragma once"); - } - out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { @@ -114,9 +110,17 @@ pub(super) fn gen( write_generic_instantiations(out, types); } - out.prepend(out.include.to_string()); - - out_file + // We collected necessary includes lazily while generating the above. Now + // put it all together. + let mut full_file = OutFile::new(namespace.clone(), header); + let full = &mut full_file; + if header { + writeln!(full, "#pragma once"); + } + write!(full, "{}", out.include); + full.next_section(); + full.extend(out); + full_file } fn write_includes(out: &mut OutFile, types: &Types) { From a05bb4ecd48660df9f22f3b879da2b63a93cabfd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 18:16:02 +0000 Subject: [PATCH 672/2232] Merge pull request #246 from dtolnay/pragma #pragma once needs to be above includes --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 4309c9c..54a359f 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -127,9 +127,6 @@ impl Display for Includes { writeln!(f, "#include ")?; writeln!(f, "#endif")?; } - if *self != Self::default() { - writeln!(f)?; - } Ok(()) } } diff --git a/gen/src/out.rs b/gen/src/out.rs index 08bf85f..f07a0d3 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -51,16 +51,15 @@ impl OutFile { } } - pub fn prepend(&mut self, section: String) { - let content = self.content.get_mut(); - content.bytes.splice(..0, section.into_bytes()); - } - pub fn write_fmt(&self, args: Arguments) { let content = &mut *self.content.borrow_mut(); Write::write_fmt(content, args).unwrap(); } + pub fn extend(&self, other: &Self) { + self.content.borrow_mut().write_bytes(&other.content.borrow().bytes); + } + pub fn content(&self) -> Vec { self.content.borrow().bytes.clone() } @@ -68,7 +67,14 @@ impl OutFile { impl Write for Content { fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { + self.write_bytes(s.as_bytes()); + Ok(()) + } +} + +impl Content { + fn write_bytes(&mut self, b: &[u8]) { + if !b.is_empty() { if !self.blocks_pending.is_empty() { if !self.bytes.is_empty() { self.bytes.push(b'\n'); @@ -84,8 +90,7 @@ impl Write for Content { } self.section_pending = false; } - self.bytes.extend_from_slice(s.as_bytes()); + self.bytes.extend_from_slice(b); } - Ok(()) } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 04d8e3e..88a0734 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -17,10 +17,6 @@ pub(super) fn gen( let mut out_file = OutFile::new(namespace.clone(), header); let out = &mut out_file; - if header { - writeln!(out, "#pragma once"); - } - out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { @@ -114,9 +110,17 @@ pub(super) fn gen( write_generic_instantiations(out, types); } - out.prepend(out.include.to_string()); - - out_file + // We collected necessary includes lazily while generating the above. Now + // put it all together. + let mut full_file = OutFile::new(namespace.clone(), header); + let full = &mut full_file; + if header { + writeln!(full, "#pragma once"); + } + write!(full, "{}", out.include); + full.next_section(); + full.extend(out); + full_file } fn write_includes(out: &mut OutFile, types: &Types) { From 54702b9839f4ebb8f0808435980afb6ae372dd42 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Jul 31 2020 18:50:09 +0000 Subject: [PATCH 673/2232] Use 'write!(out.front, ...)' to write to the front matter --- diff --git a/gen/src/out.rs b/gen/src/out.rs index f07a0d3..c21ad78 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -7,10 +7,11 @@ pub(crate) struct OutFile { pub namespace: Namespace, pub header: bool, pub include: Includes, + pub front: Content, content: RefCell, } -struct Content { +pub struct Content { bytes: Vec, section_pending: bool, blocks_pending: Vec<&'static str>, @@ -22,11 +23,8 @@ impl OutFile { namespace, header, include: Includes::new(), - content: RefCell::new(Content { - bytes: Vec::new(), - section_pending: false, - blocks_pending: Vec::new(), - }), + front: Content::new(), + content: RefCell::new(Content::new()), } } @@ -56,12 +54,17 @@ impl OutFile { Write::write_fmt(content, args).unwrap(); } - pub fn extend(&self, other: &Self) { - self.content.borrow_mut().write_bytes(&other.content.borrow().bytes); - } - pub fn content(&self) -> Vec { - self.content.borrow().bytes.clone() + let front = &self.front.bytes; + let content = &self.content.borrow().bytes; + let len = front.len() + !front.is_empty() as usize + content.len(); + let mut out = Vec::with_capacity(len); + out.extend_from_slice(front); + if !front.is_empty() { + out.push(b'\n'); + } + out.extend_from_slice(content); + out } } @@ -73,6 +76,18 @@ impl Write for Content { } impl Content { + pub fn write_fmt(&mut self, args: Arguments) { + Write::write_fmt(self, args).unwrap(); + } + + fn new() -> Self { + Content { + bytes: Vec::new(), + section_pending: false, + blocks_pending: Vec::new(), + } + } + fn write_bytes(&mut self, b: &[u8]) { if !b.is_empty() { if !self.blocks_pending.is_empty() { diff --git a/gen/src/write.rs b/gen/src/write.rs index 88a0734..ae90047 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -17,6 +17,10 @@ pub(super) fn gen( let mut out_file = OutFile::new(namespace.clone(), header); let out = &mut out_file; + if header { + writeln!(out.front, "#pragma once"); + } + out.include.extend(opt.include); for api in apis { if let Api::Include(include) = api { @@ -110,17 +114,9 @@ pub(super) fn gen( write_generic_instantiations(out, types); } - // We collected necessary includes lazily while generating the above. Now - // put it all together. - let mut full_file = OutFile::new(namespace.clone(), header); - let full = &mut full_file; - if header { - writeln!(full, "#pragma once"); - } - write!(full, "{}", out.include); - full.next_section(); - full.extend(out); - full_file + write!(out.front, "{}", out.include); + + out_file } fn write_includes(out: &mut OutFile, types: &Types) { From a1f890b46877ce06259a44eb7b10faaa7678bd5b Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Jul 31 2020 22:35:34 +0000 Subject: [PATCH 674/2232] Writing include guards around each type. This avoids problems with duplicate definitions when the generated .cc ends up including the generated .h via intermediate includes. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 88a0734..098137e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -58,20 +58,20 @@ pub(super) fn gen( match api { Api::Struct(strct) => { out.next_section(); - write_struct(out, strct); + write_struct(out, strct, namespace); } Api::Enum(enm) => { out.next_section(); if types.cxx.contains(&enm.ident) { check_enum(out, enm); } else { - write_enum(out, enm); + write_enum(out, enm, namespace); } } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { out.next_section(); - write_struct_with_methods(out, ety, methods); + write_struct_with_methods(out, ety, methods, namespace); } } _ => {} @@ -317,7 +317,8 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_struct(out: &mut OutFile, strct: &Struct) { +fn write_struct(out: &mut OutFile, strct: &Struct, namespace: &Namespace) { + write_include_guard_start(out, namespace, &strct.ident); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -328,6 +329,7 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "{};", field.ident); } writeln!(out, "}};"); + write_include_guard_end(out, namespace, &strct.ident); } fn write_struct_decl(out: &mut OutFile, ident: &Ident) { @@ -338,7 +340,36 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { +const INCLUDE_GUARD_PREFIX: &'static str = "CXXBRIDGE03_TYPE_"; + +fn write_include_guard_start(out: &mut OutFile, namespace: &Namespace, ident: &Ident) { + writeln!( + out, + "#ifndef {}{}{}", + INCLUDE_GUARD_PREFIX, namespace, ident + ); + writeln!( + out, + "#define {}{}{}", + INCLUDE_GUARD_PREFIX, namespace, ident + ); +} + +fn write_include_guard_end(out: &mut OutFile, namespace: &Namespace, ident: &Ident) { + writeln!( + out, + "#endif // {}{}{}", + INCLUDE_GUARD_PREFIX, namespace, ident + ); +} + +fn write_struct_with_methods( + out: &mut OutFile, + ety: &ExternType, + methods: &[&ExternFn], + namespace: &Namespace, +) { + write_include_guard_start(out, namespace, &ety.ident); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -353,9 +384,11 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, ";"); } writeln!(out, "}};"); + write_include_guard_end(out, namespace, &ety.ident); } -fn write_enum(out: &mut OutFile, enm: &Enum) { +fn write_enum(out: &mut OutFile, enm: &Enum, namespace: &Namespace) { + write_include_guard_start(out, namespace, &enm.ident); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -366,6 +399,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); + write_include_guard_end(out, namespace, &enm.ident); } fn check_enum(out: &mut OutFile, enm: &Enum) { From 3be1a7fc534ce26bb123306162653eeee957c47b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 01 2020 01:29:15 +0000 Subject: [PATCH 675/2232] Merge pull request #245 from dtolnay/clap Switch cli arg parsing from structopt to clap --- diff --git a/BUCK b/BUCK index 241d7b3..c554e1f 100644 --- a/BUCK +++ b/BUCK @@ -17,10 +17,10 @@ rust_binary( visibility = ["PUBLIC"], deps = [ "//third-party:anyhow", + "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", - "//third-party:structopt", "//third-party:syn", ], ) diff --git a/BUILD b/BUILD index 4d2b33a..d5255a8 100644 --- a/BUILD +++ b/BUILD @@ -20,10 +20,10 @@ rust_binary( visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", + "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", "//third-party:quote", - "//third-party:structopt", "//third-party:syn", ], ) diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 7a80a2f..b9d4b78 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -15,10 +15,10 @@ path = "src/main.rs" [dependencies] anyhow = "1.0" +clap = "2.33" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } -structopt = "0.3" syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs new file mode 100644 index 0000000..37934e5 --- /dev/null +++ b/gen/cmd/src/app.rs @@ -0,0 +1,112 @@ +use super::Opt; +use clap::AppSettings; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +type App = clap::App<'static, 'static>; +type Arg = clap::Arg<'static, 'static>; + +const USAGE: &str = "\ + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit rust/cxx.h header to stdout\ +"; + +const TEMPLATE: &str = "\ +{bin} {version} +David Tolnay +https://github.com/dtolnay/cxx + +USAGE: + {usage} + +ARGS: +{positionals} +OPTIONS: +{unified}\ +"; + +fn app() -> App { + let mut app = App::new("cxxbridge") + .usage(USAGE) + .template(TEMPLATE) + .setting(AppSettings::NextLineHelp) + .arg(arg_input()) + .arg(arg_cxx_impl_annotations()) + .arg(arg_header()) + .arg(arg_include()) + .help_message("Print help information.") + .version_message("Print version information."); + if let Some(version) = option_env!("CARGO_PKG_VERSION") { + app = app.version(version); + } + app +} + +const INPUT: &str = "input"; +const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; +const HEADER: &str = "header"; +const INCLUDE: &str = "include"; + +pub(super) fn from_args() -> Opt { + let matches = app().get_matches(); + Opt { + input: matches.value_of_os(INPUT).map(PathBuf::from), + cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned), + header: matches.is_present(HEADER), + include: matches + .values_of(INCLUDE) + .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()), + } +} + +fn validate_utf8(arg: &OsStr) -> Result<(), OsString> { + if arg.to_str().is_some() { + Ok(()) + } else { + Err(OsString::from("invalid utf-8 sequence")) + } +} + +fn arg_input() -> Arg { + Arg::with_name(INPUT) + .help("Input Rust source file containing #[cxx::bridge].") + .required_unless(HEADER) +} + +fn arg_cxx_impl_annotations() -> Arg { + const HELP: &str = "\ +Optional annotation for implementations of C++ function wrappers +that may be exposed to Rust. You may for example need to provide +__declspec(dllexport) or __attribute__((visibility(\"default\"))) +if Rust code from one shared object or executable depends on +these C++ functions in another. + "; + Arg::with_name(CXX_IMPL_ANNOTATIONS) + .long(CXX_IMPL_ANNOTATIONS) + .takes_value(true) + .value_name("annotation") + .validator_os(validate_utf8) + .help(HELP) +} + +fn arg_header() -> Arg { + Arg::with_name(HEADER) + .long(HEADER) + .help("Emit header with declarations only.") +} + +fn arg_include() -> Arg { + const HELP: &str = "\ +Any additional headers to #include. The cxxbridge tool does not +parse or even require the given paths to exist; they simply go +into the generated C++ code as #include lines. + "; + Arg::with_name(INCLUDE) + .long(INCLUDE) + .short("i") + .takes_value(true) + .multiple(true) + .validator_os(validate_utf8) + .help(HELP) +} diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 701f77f..0160913 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -6,46 +6,19 @@ clippy::toplevel_ref_arg )] +mod app; mod gen; mod syntax; use gen::include; use std::io::{self, Write}; use std::path::PathBuf; -use structopt::StructOpt; -#[derive(StructOpt, Debug)] -#[structopt( - name = "cxxbridge", - author = "David Tolnay ", - about = "https://github.com/dtolnay/cxx", - usage = "\ - cxxbridge .rs Emit .cc file for bridge to stdout - cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout", - help_message = "Print help information", - version_message = "Print version information" -)] +#[derive(Debug)] struct Opt { - /// Input Rust source file containing #[cxx::bridge] - #[structopt(parse(from_os_str), required_unless = "header")] input: Option, - - /// Emit header with declarations only - #[structopt(long)] header: bool, - - /// Optional annotation for implementations of C++ function - /// wrappers that may be exposed to Rust. You may for example - /// need to provide __declspec(dllexport) or - /// __attribute__((visibility("default"))) if Rust code from - /// one shared object or executable depends on these C++ functions - /// in another. - #[structopt(long)] cxx_impl_annotations: Option, - - /// Any additional headers to #include - #[structopt(short, long)] include: Vec, } @@ -54,7 +27,7 @@ fn write(content: impl AsRef<[u8]>) { } fn main() { - let opt = Opt::from_args(); + let opt = app::from_args(); let gen = gen::Opt { include: opt.include, diff --git a/third-party/BUCK b/third-party/BUCK index 9890a71..78fb2a9 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -22,6 +22,7 @@ rust_library( name = "clap", srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", + visibility = ["PUBLIC"], deps = [ ":bitflags", ":textwrap", @@ -40,13 +41,6 @@ rust_library( ) rust_library( - name = "heck", - srcs = glob(["vendor/heck-0.3.1/src/**"]), - edition = "2015", - deps = [":unicode-segmentation"], -) - -rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), ) @@ -58,30 +52,6 @@ rust_library( ) rust_library( - name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), - rustc_flags = ["--cfg=use_fallback"], - deps = [ - ":proc-macro-error-attr", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( - name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), - proc_macro = True, - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ":syn-mid", - ], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), visibility = ["PUBLIC"], @@ -106,30 +76,6 @@ rust_library( ) rust_library( - name = "structopt", - srcs = glob(["vendor/structopt-0.3.15/src/**"]), - visibility = ["PUBLIC"], - deps = [ - ":clap", - ":lazy_static", - ":structopt-derive", - ], -) - -rust_library( - name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), - proc_macro = True, - deps = [ - ":heck", - ":proc-macro-error", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.36/src/**"]), visibility = ["PUBLIC"], @@ -149,16 +95,6 @@ rust_library( ) rust_library( - name = "syn-mid", - srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "termcolor", srcs = glob(["vendor/termcolor-1.1.0/src/**"]), ) @@ -170,12 +106,6 @@ rust_library( ) rust_library( - name = "unicode-segmentation", - srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), - edition = "2015", -) - -rust_library( name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) diff --git a/third-party/BUILD b/third-party/BUILD index a01d4b7..3e0e30b 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -27,6 +27,7 @@ rust_library( name = "clap", srcs = glob(["vendor/clap-2.33.1/src/**"]), edition = "2015", + visibility = ["//visibility:public"], deps = [ ":bitflags", ":textwrap", @@ -45,13 +46,6 @@ rust_library( ) rust_library( - name = "heck", - srcs = glob(["vendor/heck-0.3.1/src/**"]), - edition = "2015", - deps = [":unicode-segmentation"], -) - -rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), ) @@ -63,32 +57,6 @@ rust_library( ) rust_library( - name = "proc-macro-error", - srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]), - proc_macro_deps = [ - ":proc-macro-error-attr", - ], - rustc_flags = ["--cfg=use_fallback"], - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( - name = "proc-macro-error-attr", - srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]), - crate_type = "proc-macro", - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ":syn-mid", - ], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), crate_features = [ @@ -113,32 +81,6 @@ rust_library( ) rust_library( - name = "structopt", - srcs = glob(["vendor/structopt-0.3.15/src/**"]), - proc_macro_deps = [ - ":structopt-derive", - ], - visibility = ["//visibility:public"], - deps = [ - ":clap", - ":lazy_static", - ], -) - -rust_library( - name = "structopt-derive", - srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]), - crate_type = "proc-macro", - deps = [ - ":heck", - ":proc-macro-error", - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.36/src/**"]), crate_features = [ @@ -158,16 +100,6 @@ rust_library( ) rust_library( - name = "syn-mid", - srcs = glob(["vendor/syn-mid-0.5.0/src/**"]), - deps = [ - ":proc-macro2", - ":quote", - ":syn", - ], -) - -rust_library( name = "termcolor", srcs = glob(["vendor/termcolor-1.1.0/src/**"]), ) @@ -179,12 +111,6 @@ rust_library( ) rust_library( - name = "unicode-segmentation", - srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]), - edition = "2015", -) - -rust_library( name = "unicode-width", srcs = glob(["vendor/unicode-width-0.1.8/src/**"]), ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 8f40664..521c030 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -101,10 +101,10 @@ name = "cxxbridge-cmd" version = "0.3.4" dependencies = [ "anyhow", + "clap", "codespan-reporting", "proc-macro2", "quote", - "structopt", "syn", ] @@ -139,15 +139,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - -[[package]] name = "hermit-abi" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -184,32 +175,6 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syn-mid", - "version_check", -] - -[[package]] name = "proc-macro2" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -282,30 +247,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] -name = "structopt" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "syn" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -317,17 +258,6 @@ dependencies = [ ] [[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -370,12 +300,6 @@ dependencies = [ ] [[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -394,12 +318,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" From df4ca02003c07ed6e008ef5ceb2c824e1da18076 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 01 2020 01:33:44 +0000 Subject: [PATCH 676/2232] Access namespace via OutFile --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 098137e..e088e2c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -58,20 +58,20 @@ pub(super) fn gen( match api { Api::Struct(strct) => { out.next_section(); - write_struct(out, strct, namespace); + write_struct(out, strct); } Api::Enum(enm) => { out.next_section(); if types.cxx.contains(&enm.ident) { check_enum(out, enm); } else { - write_enum(out, enm, namespace); + write_enum(out, enm); } } Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident) { out.next_section(); - write_struct_with_methods(out, ety, methods, namespace); + write_struct_with_methods(out, ety, methods); } } _ => {} @@ -317,8 +317,8 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_struct(out: &mut OutFile, strct: &Struct, namespace: &Namespace) { - write_include_guard_start(out, namespace, &strct.ident); +fn write_struct(out: &mut OutFile, strct: &Struct) { + write_include_guard_start(out, &strct.ident); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -329,7 +329,7 @@ fn write_struct(out: &mut OutFile, strct: &Struct, namespace: &Namespace) { writeln!(out, "{};", field.ident); } writeln!(out, "}};"); - write_include_guard_end(out, namespace, &strct.ident); + write_include_guard_end(out, &strct.ident); } fn write_struct_decl(out: &mut OutFile, ident: &Ident) { @@ -342,34 +342,29 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { const INCLUDE_GUARD_PREFIX: &'static str = "CXXBRIDGE03_TYPE_"; -fn write_include_guard_start(out: &mut OutFile, namespace: &Namespace, ident: &Ident) { +fn write_include_guard_start(out: &mut OutFile, ident: &Ident) { writeln!( out, "#ifndef {}{}{}", - INCLUDE_GUARD_PREFIX, namespace, ident + INCLUDE_GUARD_PREFIX, out.namespace, ident ); writeln!( out, "#define {}{}{}", - INCLUDE_GUARD_PREFIX, namespace, ident + INCLUDE_GUARD_PREFIX, out.namespace, ident ); } -fn write_include_guard_end(out: &mut OutFile, namespace: &Namespace, ident: &Ident) { +fn write_include_guard_end(out: &mut OutFile, ident: &Ident) { writeln!( out, "#endif // {}{}{}", - INCLUDE_GUARD_PREFIX, namespace, ident + INCLUDE_GUARD_PREFIX, out.namespace, ident ); } -fn write_struct_with_methods( - out: &mut OutFile, - ety: &ExternType, - methods: &[&ExternFn], - namespace: &Namespace, -) { - write_include_guard_start(out, namespace, &ety.ident); +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { + write_include_guard_start(out, &ety.ident); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -384,11 +379,11 @@ fn write_struct_with_methods( writeln!(out, ";"); } writeln!(out, "}};"); - write_include_guard_end(out, namespace, &ety.ident); + write_include_guard_end(out, &ety.ident); } -fn write_enum(out: &mut OutFile, enm: &Enum, namespace: &Namespace) { - write_include_guard_start(out, namespace, &enm.ident); +fn write_enum(out: &mut OutFile, enm: &Enum) { + write_include_guard_start(out, &enm.ident); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -399,7 +394,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum, namespace: &Namespace) { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); - write_include_guard_end(out, namespace, &enm.ident); + write_include_guard_end(out, &enm.ident); } fn check_enum(out: &mut OutFile, enm: &Enum) { From 8cfdd7ddd69c7dc326c9014cd585a9df1de01a7c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 01 2020 02:05:48 +0000 Subject: [PATCH 677/2232] Merge pull request #247 from adetaylor/include-guards Writing include guards around each type. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index ae90047..51c4b02 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -314,6 +314,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { + write_include_guard_start(out, &strct.ident); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -324,6 +325,7 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "{};", field.ident); } writeln!(out, "}};"); + write_include_guard_end(out, &strct.ident); } fn write_struct_decl(out: &mut OutFile, ident: &Ident) { @@ -334,7 +336,31 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } +const INCLUDE_GUARD_PREFIX: &'static str = "CXXBRIDGE03_TYPE_"; + +fn write_include_guard_start(out: &mut OutFile, ident: &Ident) { + writeln!( + out, + "#ifndef {}{}{}", + INCLUDE_GUARD_PREFIX, out.namespace, ident + ); + writeln!( + out, + "#define {}{}{}", + INCLUDE_GUARD_PREFIX, out.namespace, ident + ); +} + +fn write_include_guard_end(out: &mut OutFile, ident: &Ident) { + writeln!( + out, + "#endif // {}{}{}", + INCLUDE_GUARD_PREFIX, out.namespace, ident + ); +} + fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { + write_include_guard_start(out, &ety.ident); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -349,9 +375,11 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, ";"); } writeln!(out, "}};"); + write_include_guard_end(out, &ety.ident); } fn write_enum(out: &mut OutFile, enm: &Enum) { + write_include_guard_start(out, &enm.ident); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -362,6 +390,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); + write_include_guard_end(out, &enm.ident); } fn check_enum(out: &mut OutFile, enm: &Enum) { From 291a8b87a16e0c738b8caaeae1d28670416dda9e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 09 2020 23:46:45 +0000 Subject: [PATCH 678/2232] Update to trybuild 1.0.32 --- diff --git a/Cargo.toml b/Cargo.toml index 6a8c1a4..1e0ad0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ cc = "1.0.49" cxx-build = { version = "=0.3.4", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = { version = "1.0.27", features = ["diff"] } +trybuild = { version = "1.0.32", features = ["diff"] } [workspace] members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index cb3ae15..e448a22 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -4,9 +4,9 @@ error[E0271]: type mismatch resolving `::I 11 | type ByteRange = crate::here::StringPiece; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements | - ::: $WORKSPACE/src/extern_type.rs:110:41 + ::: $WORKSPACE/src/extern_type.rs | -110 | pub fn verify_extern_type, Id>() {} + | pub fn verify_extern_type, Id>() {} | ------- required by this bound in `cxx::private::verify_extern_type` | = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 521c030..763a429 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4d94e6adf00b96b1ab94fcfcd8c3cf916733b39adf90c8f72693629887b9b8" +checksum = "d3fe571fc1c805a7dc57340d2be54d72a96010a46a05c57018863f3b1cb93d28" dependencies = [ "dissimilar", "glob", From 9e48d5b5c6f88477106cd34b990c3ea5eda2cc09 Mon Sep 17 00:00:00 2001 From: Stephen Crane Date: Aug 21 2020 19:17:02 +0000 Subject: [PATCH 679/2232] Add rust::Vec accessors Adds operator[], at(), front(), and back() to rust::Vec. --- diff --git a/include/cxx.h b/include/cxx.h index c3231fd..786dbb3 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,26 @@ #include #endif +#ifndef __has_feature +#define __has_feature(__x) 0 +#endif + +#if !__has_feature(cxx_exceptions) +# define _CXXBRIDGE03_NO_EXCEPTIONS +#endif +#if !__EXCEPTIONS +# define _CXXBRIDGE03_NO_EXCEPTIONS +#endif + +[[noreturn]] inline static void throw_out_of_range(const char *msg) { +#ifndef _CXXBRIDGE03_NO_EXCEPTIONS + throw std::out_of_range(msg); +#else + ((void)msg); + std::abort(); +#endif +} + namespace rust { inline namespace cxxbridge03 { @@ -175,6 +196,12 @@ public: bool empty() const noexcept; const T *data() const noexcept; + const T &operator[](size_t n) const noexcept; + const T &at(size_t n) const; + + const T &front() const; + const T &back() const; + class const_iterator { public: using difference_type = ptrdiff_t; @@ -464,6 +491,29 @@ bool Vec::empty() const noexcept { } template +const T &Vec::operator[](size_t n) const noexcept { + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * this->stride()); +} + +template +const T &Vec::at(size_t n) const { + if (n >= this->size()) + throw_out_of_range("Vec"); + return (*this)[n]; +} + +template +const T &Vec::front() const { + return (*this)[0]; +} + +template +const T &Vec::back() const { + return (*this)[this->size()-1]; +} + +template const T &Vec::const_iterator::operator*() const noexcept { return *static_cast(this->pos); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 84a746b..e491343 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -11,6 +11,7 @@ use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] pub mod ffi { + #[derive(Clone)] struct Shared { z: usize, } @@ -61,8 +62,11 @@ pub mod ffi { fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); fn c_take_rust_vec_shared(v: Vec); + fn c_take_rust_vec_index(v: Vec); + fn c_take_rust_vec_shared_index(v: Vec); fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); + fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); /* // https://github.com/dtolnay/cxx/issues/232 diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d70d5a0..76fe8b7 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -208,6 +208,8 @@ void c_take_ref_vector(const std::vector &v) { void c_take_rust_vec(rust::Vec v) { c_take_ref_rust_vec(v); } +void c_take_rust_vec_index(rust::Vec v) { c_take_ref_rust_vec_index(v); } + void c_take_rust_vec_shared(rust::Vec v) { uint32_t sum = 0; for (auto i : v) { @@ -230,6 +232,17 @@ void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { } } +void c_take_rust_vec_shared_index(rust::Vec v) { + if (v[0].z == 1010 && + v.at(0).z == 1010 && + v.front().z == 1010 && + v[1].z == 1011 && + v.at(1).z == 1011 && + v.back().z == 1011) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_rust_vec(const rust::Vec &v) { uint8_t sum = std::accumulate(v.begin(), v.end(), 0); if (sum == 200) { @@ -237,6 +250,19 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } +void c_take_ref_rust_vec_index(const rust::Vec &v) { + if (v[0] == 86 && + v.at(0) == 86 && + v.front() == 86 && + v[1] == 75 && + v.at(1) == 75 && + v[3] == 9 && + v.at(3) == 9 && + v.back() == 9) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_rust_vec_copy(const rust::Vec &v) { // The std::copy() will make sure rust::Vec<>::const_iterator satisfies the // requirements for std::iterator_traits. diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7121cb2..7b7d273 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -65,9 +65,12 @@ void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); +void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); +void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); /* // https://github.com/dtolnay/cxx/issues/232 diff --git a/tests/test.rs b/tests/test.rs index f2f670c..55c28d2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -108,15 +108,16 @@ fn test_c_take() { check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec(); check!(ffi::c_take_rust_vec(test_vec.clone())); - check!(ffi::c_take_rust_vec_shared(vec![ + check!(ffi::c_take_rust_vec_index(test_vec.clone())); + let shared_test_vec = vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } - ])); - check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![ - ffi::Shared { z: 1010 }, - ffi::Shared { z: 1011 } - ])); + ]; + check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone())); + check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone())); + check!(ffi::c_take_rust_vec_shared_forward_iterator(shared_test_vec)); check!(ffi::c_take_ref_rust_vec(&test_vec)); + check!(ffi::c_take_ref_rust_vec_index(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); check!(ffi::c_take_enum(ffi::Enum::AVal)); } From 9fc0846261d66412f95ab6d9dc5b319491641926 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Aug 22 2020 06:27:29 +0000 Subject: [PATCH 680/2232] Adding library for high-level code generators. The intention here is to provide a way for high level code generators to convert a Rust TokenStream into C++ bindings code without the need to write files to disk or invoke an external command. --- diff --git a/Cargo.toml b/Cargo.toml index 1e0ad0f..23d7cf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ rustversion = "1.0" trybuild = { version = "1.0.32", features = ["diff"] } [workspace] -members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] +members = ["demo-rs", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/README.md b/gen/README.md index 9786911..fc8d10f 100644 --- a/gen/README.md +++ b/gen/README.md @@ -1,4 +1,5 @@ -This directory contains CXX's C++ code generator. This code generator has two +This directory contains CXX's C++ code generator. This code generator has three public frontends, one a command-line application (binary) in the *cmd* directory -and the other a library intended to be used from a build.rs in the *build* -directory. +a library intended to be used from a build.rs in the *build* directory, and +a library intended to be used from arbitrary other places (e.g. higher-level +code generators) in the *lib* directory. diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml new file mode 100644 index 0000000..3a5d938 --- /dev/null +++ b/gen/lib/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cxx-gen" +version = "0.3.4" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into higher level tools." +repository = "https://github.com/dtolnay/cxx" +keywords = ["ffi"] +categories = ["development-tools::ffi"] + +[dependencies] +anyhow = "1.0" +cc = "1.0.49" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } +quote = { version = "1.0", default-features = false } +syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/LICENSE-APACHE b/gen/lib/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/lib/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/lib/LICENSE-MIT b/gen/lib/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/lib/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/lib/src/gen b/gen/lib/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/lib/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs new file mode 100644 index 0000000..ba125d9 --- /dev/null +++ b/gen/lib/src/lib.rs @@ -0,0 +1,17 @@ +//! The CXX code generator for constructing and compiling C++ code. +//! +//! This is intended to be embedded into higher-level code generators. + +mod gen; +mod syntax; + +use crate::gen::Opt; +use proc_macro2::TokenStream; + +pub use crate::gen::{Error, Result, GeneratedCode}; + +/// Generate C++ bindings code from a Rust token stream. This should be a Rust +/// token stream which somewhere contains a `#[cxx::bridge] mod {}`. +pub fn generate_header_and_cc(rust_source: TokenStream) -> Result { + gen::do_generate_from_tokens(rust_source, Opt::default()) +} diff --git a/gen/lib/src/syntax b/gen/lib/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/lib/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/src/error.rs b/gen/src/error.rs index 51dbbe7..61afd4d 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -11,13 +11,18 @@ use std::ops::Range; use std::path::Path; use std::process; -pub(super) type Result = std::result::Result; +pub type Result = std::result::Result; #[derive(Debug)] -pub(super) enum Error { +pub enum Error { + /// No `#[cxx::bridge]` module could be found. NoBridgeMod, + /// `#[cxx::bridge]` was attached to something other than + /// an inline module. OutOfLineMod, + /// An IO error occurred when reading Rust code. Io(io::Error), + /// A syntax error occurred when parsing Rust code. Syn(syn::Error), } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 710bca2..acc7669 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -10,20 +10,23 @@ mod write; #[cfg(test)] mod tests; -use self::error::{format_err, Error, Result}; +use self::error::format_err; +pub use self::error::{Error, Result}; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; +use proc_macro2::TokenStream; +use std::clone::Clone; use std::fs; use std::path::Path; -use syn::Item; +use syn::{File, Item}; struct Input { namespace: Namespace, module: Vec, } -#[derive(Default)] +#[derive(Default, Clone)] pub(super) struct Opt { /// Any additional headers to #include pub include: Vec, @@ -32,6 +35,14 @@ pub(super) struct Opt { pub cxx_impl_annotations: Option, } +/// Results of code generation. +pub struct GeneratedCode { + /// The bytes of a C++ header file. + pub header: Vec, + /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) + pub cxx: Vec, +} + pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; generate_from_path(path, opt, header) @@ -42,21 +53,43 @@ pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { generate_from_path(path, opt, header) } +pub(super) fn do_generate_from_tokens( + tokens: TokenStream, + opt: Opt, +) -> std::result::Result { + let syntax = syn::parse2::(tokens)?; + match generate(syntax, opt, true, true) { + Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx } ), + Err(err) => Err(err), + _ => panic!("Unexpected generation"), + } +} + fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match generate(&source, opt, header) { + let syntax = match syn::parse_file(&source) { Ok(out) => out, + Err(err) => format_err(path, "", Error::Syn(err)), + }; + match generate(syntax, opt, header, !header) { + Ok((Some(hdr), None)) => hdr, + Ok((None, Some(cxx))) => cxx, Err(err) => format_err(path, &source, err), + _ => panic!("Unexpected generation"), } } -fn generate(source: &str, opt: Opt, header: bool) -> Result> { +fn generate( + syntax: File, + opt: Opt, + gen_header: bool, + gen_cxx: bool, +) -> Result<(Option>, Option>)> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; let bridge = find::find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; let ref apis = syntax::parse_items(errors, bridge.module); @@ -64,6 +97,18 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; - let out = write::gen(namespace, apis, types, opt, header); - Ok(out.content()) + // Some callers may wish to generate both header and C++ + // from the same token stream to avoid parsing twice. But others + // only need to generate one or the other. + let hdr = if gen_header { + Some(write::gen(namespace, apis, types, opt.clone(), true).content()) + } else { + None + }; + let cxx = if gen_cxx { + Some(write::gen(namespace, apis, types, opt, false).content()) + } else { + None + }; + Ok((hdr, cxx)) } From 593eddb017d6715715551a6cb79801ac21ee6daf Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Aug 22 2020 06:46:08 +0000 Subject: [PATCH 681/2232] Adding tests, fixing older tests. --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index ba125d9..de76d80 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -8,10 +8,39 @@ mod syntax; use crate::gen::Opt; use proc_macro2::TokenStream; -pub use crate::gen::{Error, Result, GeneratedCode}; +pub use crate::gen::{Error, GeneratedCode, Result}; /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. pub fn generate_header_and_cc(rust_source: TokenStream) -> Result { gen::do_generate_from_tokens(rust_source, Opt::default()) } + +#[cfg(test)] +mod test { + use quote::quote; + + #[test] + fn test_positive() { + let rs = quote! { + #[cxx::bridge] + mod ffi { + extern "C" { + fn in_C(); + } + extern "Rust" { + fn in_rs(); + } + } + }; + let code = crate::generate_header_and_cc(rs).unwrap(); + assert!(code.cxx.len() > 0); + assert!(code.header.len() > 0); + } + + #[test] + fn test_negative() { + let rs = quote! {}; + assert!(crate::generate_header_and_cc(rs).is_err()) + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index acc7669..3139ed3 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -59,7 +59,7 @@ pub(super) fn do_generate_from_tokens( ) -> std::result::Result { let syntax = syn::parse2::(tokens)?; match generate(syntax, opt, true, true) { - Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx } ), + Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), Err(err) => Err(err), _ => panic!("Unexpected generation"), } @@ -70,14 +70,18 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - let syntax = match syn::parse_file(&source) { + match generate_from_string(&source, opt, header) { Ok(out) => out, - Err(err) => format_err(path, "", Error::Syn(err)), - }; - match generate(syntax, opt, header, !header) { - Ok((Some(hdr), None)) => hdr, - Ok((None, Some(cxx))) => cxx, Err(err) => format_err(path, &source, err), + } +} + +fn generate_from_string(source: &str, opt: Opt, header: bool) -> Result> { + let syntax = syn::parse_file(&source).map_err(Error::Syn)?; + let results = generate(syntax, opt, header, !header)?; + match results { + (Some(hdr), None) => Ok(hdr), + (None, Some(cxx)) => Ok(cxx), _ => panic!("Unexpected generation"), } } diff --git a/gen/src/tests.rs b/gen/src/tests.rs index 0e7a910..7621643 100644 --- a/gen/src/tests.rs +++ b/gen/src/tests.rs @@ -1,4 +1,4 @@ -use crate::gen::{generate, Opt}; +use crate::gen::{generate_from_string, Opt}; const CPP_EXAMPLE: &'static str = r#" #[cxx::bridge] @@ -15,7 +15,7 @@ fn test_cpp() { include: Vec::new(), cxx_impl_annotations: None, }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. @@ -28,7 +28,7 @@ fn test_annotation() { include: Vec::new(), cxx_impl_annotations: Some("ANNOTATION".to_string()), }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); } From 0e79bc8fdeb4e21c5a69057c74c51f874fb35348 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 22 2020 12:57:03 +0000 Subject: [PATCH 682/2232] Update bazel build to rust 1.45 --- diff --git a/WORKSPACE b/WORKSPACE index cd2a2e8..08e7c80 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,13 +24,13 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_44_linux", + name = "rust_1_45_linux", exec_triple = "x86_64-unknown-linux-gnu", - version = "1.44.0", + version = "1.45.0", ) rust_repository_set( - name = "rust_1_44_darwin", + name = "rust_1_45_darwin", exec_triple = "x86_64-apple-darwin", - version = "1.44.0", + version = "1.45.0", ) From 85487b00f8790fe28e8d82e9dfab3b3145503631 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 22 2020 13:13:27 +0000 Subject: [PATCH 683/2232] Add Option to todo list --- diff --git a/README.md b/README.md index 260c272..345ce7d 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,7 @@ matter of designing a nice API for each in its non-native language. BTreeMap<K, V>tbd HashMap<K, V>tbd Arc<T>tbd +Option<T>tbd tbdstd::map<K, V> tbdstd::unordered_map<K, V> tbdstd::shared_ptr<T> diff --git a/src/lib.rs b/src/lib.rs index 0ad7255..91e0098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -341,6 +341,7 @@ //! BTreeMap<K, V>tbd //! HashMap<K, V>tbd //! Arc<T>tbd +//! Option<T>tbd //! tbdstd::map<K, V> //! tbdstd::unordered_map<K, V> //! tbdstd::shared_ptr<T> From 7ca810b1e81d32f4d912d0fb752aadfa056363c2 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Aug 25 2020 00:58:36 +0000 Subject: [PATCH 684/2232] Guess at BUCK and BUILD rules. --- diff --git a/BUCK b/BUCK index c554e1f..a58e0a2 100644 --- a/BUCK +++ b/BUCK @@ -68,3 +68,17 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "lib", + srcs = glob(["gen/lib/src/**"]), + visibility = ["PUBLIC"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/BUILD b/BUILD index d5255a8..017b7cc 100644 --- a/BUILD +++ b/BUILD @@ -67,3 +67,18 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "lib", + srcs = glob(["gen/lib/src/**/*.rs"]), + data = ["gen/build/src/gen/include/cxx.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) From 0926f644ee592d0d65065991fe8263c1b657cc8d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Aug 25 2020 20:08:06 +0000 Subject: [PATCH 685/2232] Allow configurable options during C++ codegen. This commit allows a higher-level code generator to pass options into the cxx code generator. --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index de76d80..cecf654 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -5,15 +5,15 @@ mod gen; mod syntax; -use crate::gen::Opt; +pub use crate::gen::Opt; use proc_macro2::TokenStream; pub use crate::gen::{Error, GeneratedCode, Result}; /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. -pub fn generate_header_and_cc(rust_source: TokenStream) -> Result { - gen::do_generate_from_tokens(rust_source, Opt::default()) +pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { + gen::do_generate_from_tokens(rust_source, opt) } #[cfg(test)] diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 3139ed3..f2e5c9b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -26,8 +26,9 @@ struct Input { module: Vec, } +/// Options for C++ code generation. #[derive(Default, Clone)] -pub(super) struct Opt { +pub struct Opt { /// Any additional headers to #include pub include: Vec, /// Whether to set __attribute__((visibility("default"))) From 5a6c7b534d33680606570ffe78b025c145fe6655 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Aug 25 2020 20:08:38 +0000 Subject: [PATCH 686/2232] Add option to omit type definitions. A higher-level code generator may provide instructions to cxx based on existing defined C++ types. In such an instance, we don't want cxx to redefine those types, because this will cause violations of the one-definition-rule. This commit provides an option to prevent such redefinition. As this is only likely to be used from higher-level code generators, we do not expose this option via 'cmd' or 'build' front-ends for code generation. --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index f2e5c9b..1c84a23 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -34,6 +34,8 @@ pub struct Opt { /// Whether to set __attribute__((visibility("default"))) /// or similar annotations on function implementations. pub cxx_impl_annotations: Option, + /// Whether to omit definitions of types. + pub omit_type_definitions: bool, } /// Results of code generation. diff --git a/gen/src/write.rs b/gen/src/write.rs index 51c4b02..b184b52 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -60,7 +60,7 @@ pub(super) fn gen( for api in apis { match api { - Api::Struct(strct) => { + Api::Struct(strct) if !opt.omit_type_definitions => { out.next_section(); write_struct(out, strct); } @@ -68,7 +68,7 @@ pub(super) fn gen( out.next_section(); if types.cxx.contains(&enm.ident) { check_enum(out, enm); - } else { + } else if !opt.omit_type_definitions { write_enum(out, enm); } } From a08b19f5b1908939642ada54a343eae339edf4dc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 26 2020 02:21:16 +0000 Subject: [PATCH 687/2232] Add include required for back_inserter See failure in https://github.com/dtolnay/cxx/issues/260. --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d70d5a0..060b8aa 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,6 +1,7 @@ #include "tests/ffi/tests.h" #include "tests/ffi/lib.rs.h" #include +#include #include #include From 691dc171b5f0f34478a02129a7e1e784c591911e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 26 2020 02:56:12 +0000 Subject: [PATCH 688/2232] Update ui tests with rust-src component installed --- diff --git a/Cargo.toml b/Cargo.toml index 1e0ad0f..234b098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ cc = "1.0.49" cxx-build = { version = "=0.3.4", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = { version = "1.0.32", features = ["diff"] } +trybuild = { version = "1.0.33", features = ["diff"] } [workspace] members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index efd1144..698fda7 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -4,5 +4,10 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t 4 | type TypeR; | ^^^^^ doesn't have a size known at compile-time | + ::: $RUST/core/src/ptr/mod.rs + | + | pub unsafe fn read(src: *const T) -> T { + | - required by this bound in `std::ptr::read` + | = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` = note: required because it appears within the type `TypeR` From c8361027d982ddf07c28a4bba1ff20a8618e1955 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 26 2020 05:03:00 +0000 Subject: [PATCH 689/2232] Remove dependency of ui test on whether rust-src is installed --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index f28d955..431fd3b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -31,11 +31,7 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T for api in apis { if let Api::RustType(ety) = api { expanded.extend(expand_rust_type(ety)); - let ident = &ety.ident; - let span = ident.span(); - hidden.extend(quote_spanned! {span=> - let _ = ::std::ptr::read::<#ident>; - }); + hidden.extend(expand_rust_type_assert_sized(ety)); } } @@ -426,6 +422,28 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream { } } +fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { + // Rustc will render as follows if not sized: + // + // type TheirType; + // -----^^^^^^^^^- + // | | + // | doesn't have a size known at compile-time + // required by this bound in `ffi::_::__AssertSized` + + let ident = &ety.ident; + let begin_span = Token![::](ety.type_token.span); + let sized = quote_spanned! {ety.semi_token.span=> + #begin_span std::marker::Sized + }; + quote_spanned! {ident.span()=> + let _ = { + fn __AssertSized() {} + __AssertSized::<#ident> + }; + } +} + fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { let ident = &efn.ident; let link_name = mangle::extern_fn(namespace, efn); diff --git a/syntax/mod.rs b/syntax/mod.rs index 88c96e8..cd6f235 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -46,6 +46,7 @@ pub struct ExternType { pub doc: Doc, pub type_token: Token![type], pub ident: Ident, + pub semi_token: Token![;], } pub struct Struct { diff --git a/syntax/parse.rs b/syntax/parse.rs index a1d31cf..e195081 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -242,6 +242,7 @@ fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang let doc = attrs::parse_doc(cx, &foreign_type.attrs); let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); + let semi_token = foreign_type.semi_token; let api_type = match lang { Lang::Cxx => Api::CxxType, Lang::Rust => Api::RustType, @@ -250,6 +251,7 @@ fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang doc, type_token, ident, + semi_token, })) } diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 698fda7..366a8f4 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -1,13 +1,11 @@ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/opaque_not_sized.rs:4:14 - | -4 | type TypeR; - | ^^^^^ doesn't have a size known at compile-time - | - ::: $RUST/core/src/ptr/mod.rs - | - | pub unsafe fn read(src: *const T) -> T { - | - required by this bound in `std::ptr::read` - | - = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` - = note: required because it appears within the type `TypeR` + --> $DIR/opaque_not_sized.rs:4:14 + | +4 | type TypeR; + | -----^^^^^- + | | | + | | doesn't have a size known at compile-time + | required by this bound in `ffi::_::__AssertSized` + | + = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` + = note: required because it appears within the type `TypeR` From 041c9040ba4f678e87622ea59515e16f35eeb1e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 26 2020 05:16:15 +0000 Subject: [PATCH 690/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 78fb2a9..585dafd 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -14,13 +14,13 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.58/src/**"]), + srcs = glob(["vendor/cc-1.0.59/src/**"]), visibility = ["PUBLIC"], ) rust_library( name = "clap", - srcs = glob(["vendor/clap-2.33.1/src/**"]), + srcs = glob(["vendor/clap-2.33.3/src/**"]), edition = "2015", visibility = ["PUBLIC"], deps = [ @@ -77,7 +77,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.36/src/**"]), + srcs = glob(["vendor/syn-1.0.39/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 3e0e30b..65edf92 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -19,13 +19,13 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.58/src/**"]), + srcs = glob(["vendor/cc-1.0.59/src/**"]), visibility = ["//visibility:public"], ) rust_library( name = "clap", - srcs = glob(["vendor/clap-2.33.1/src/**"]), + srcs = glob(["vendor/clap-2.33.3/src/**"]), edition = "2015", visibility = ["//visibility:public"], deps = [ @@ -82,7 +82,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.36/src/**"]), + srcs = glob(["vendor/syn-1.0.39/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 763a429..10c577d 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -34,15 +34,15 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" [[package]] name = "clap" -version = "2.33.1" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", @@ -161,9 +161,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.74" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" +checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" [[package]] name = "link-cplusplus" @@ -211,18 +211,18 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" dependencies = [ "proc-macro2", "quote", @@ -248,9 +248,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.36" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" +checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" dependencies = [ "proc-macro2", "quote", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fe571fc1c805a7dc57340d2be54d72a96010a46a05c57018863f3b1cb93d28" +checksum = "48105a4deaf74163c017939b45ef7322fba46e8b17281528039b0beb04235e92" dependencies = [ "dissimilar", "glob", From fb8ddd8eb398f41b09f5befd46250dc9e4c00455 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 04:49:23 +0000 Subject: [PATCH 691/2232] Merge pull request #257 from rinon/rust_vec_accessors Add rust::Vec accessors --- diff --git a/include/cxx.h b/include/cxx.h index c3231fd..786dbb3 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,26 @@ #include #endif +#ifndef __has_feature +#define __has_feature(__x) 0 +#endif + +#if !__has_feature(cxx_exceptions) +# define _CXXBRIDGE03_NO_EXCEPTIONS +#endif +#if !__EXCEPTIONS +# define _CXXBRIDGE03_NO_EXCEPTIONS +#endif + +[[noreturn]] inline static void throw_out_of_range(const char *msg) { +#ifndef _CXXBRIDGE03_NO_EXCEPTIONS + throw std::out_of_range(msg); +#else + ((void)msg); + std::abort(); +#endif +} + namespace rust { inline namespace cxxbridge03 { @@ -175,6 +196,12 @@ public: bool empty() const noexcept; const T *data() const noexcept; + const T &operator[](size_t n) const noexcept; + const T &at(size_t n) const; + + const T &front() const; + const T &back() const; + class const_iterator { public: using difference_type = ptrdiff_t; @@ -464,6 +491,29 @@ bool Vec::empty() const noexcept { } template +const T &Vec::operator[](size_t n) const noexcept { + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * this->stride()); +} + +template +const T &Vec::at(size_t n) const { + if (n >= this->size()) + throw_out_of_range("Vec"); + return (*this)[n]; +} + +template +const T &Vec::front() const { + return (*this)[0]; +} + +template +const T &Vec::back() const { + return (*this)[this->size()-1]; +} + +template const T &Vec::const_iterator::operator*() const noexcept { return *static_cast(this->pos); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 84a746b..e491343 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -11,6 +11,7 @@ use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] pub mod ffi { + #[derive(Clone)] struct Shared { z: usize, } @@ -61,8 +62,11 @@ pub mod ffi { fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); fn c_take_rust_vec_shared(v: Vec); + fn c_take_rust_vec_index(v: Vec); + fn c_take_rust_vec_shared_index(v: Vec); fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); + fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); /* // https://github.com/dtolnay/cxx/issues/232 diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 060b8aa..66200e5 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -209,6 +209,8 @@ void c_take_ref_vector(const std::vector &v) { void c_take_rust_vec(rust::Vec v) { c_take_ref_rust_vec(v); } +void c_take_rust_vec_index(rust::Vec v) { c_take_ref_rust_vec_index(v); } + void c_take_rust_vec_shared(rust::Vec v) { uint32_t sum = 0; for (auto i : v) { @@ -231,6 +233,17 @@ void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { } } +void c_take_rust_vec_shared_index(rust::Vec v) { + if (v[0].z == 1010 && + v.at(0).z == 1010 && + v.front().z == 1010 && + v[1].z == 1011 && + v.at(1).z == 1011 && + v.back().z == 1011) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_rust_vec(const rust::Vec &v) { uint8_t sum = std::accumulate(v.begin(), v.end(), 0); if (sum == 200) { @@ -238,6 +251,19 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } +void c_take_ref_rust_vec_index(const rust::Vec &v) { + if (v[0] == 86 && + v.at(0) == 86 && + v.front() == 86 && + v[1] == 75 && + v.at(1) == 75 && + v[3] == 9 && + v.at(3) == 9 && + v.back() == 9) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_rust_vec_copy(const rust::Vec &v) { // The std::copy() will make sure rust::Vec<>::const_iterator satisfies the // requirements for std::iterator_traits. diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7121cb2..7b7d273 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -65,9 +65,12 @@ void c_take_unique_ptr_vector_f64(std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); +void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); +void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); /* // https://github.com/dtolnay/cxx/issues/232 diff --git a/tests/test.rs b/tests/test.rs index f2f670c..55c28d2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -108,15 +108,16 @@ fn test_c_take() { check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec(); check!(ffi::c_take_rust_vec(test_vec.clone())); - check!(ffi::c_take_rust_vec_shared(vec![ + check!(ffi::c_take_rust_vec_index(test_vec.clone())); + let shared_test_vec = vec![ ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 } - ])); - check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![ - ffi::Shared { z: 1010 }, - ffi::Shared { z: 1011 } - ])); + ]; + check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone())); + check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone())); + check!(ffi::c_take_rust_vec_shared_forward_iterator(shared_test_vec)); check!(ffi::c_take_ref_rust_vec(&test_vec)); + check!(ffi::c_take_ref_rust_vec_index(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); check!(ffi::c_take_enum(ffi::Enum::AVal)); } From 521d99d0930abd3171ff4296a548e8a9143723ea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 04:50:11 +0000 Subject: [PATCH 692/2232] Unify the way that String construction and Vec indexing throw --- diff --git a/include/cxx.h b/include/cxx.h index 786dbb3..d3f6238 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -14,26 +14,6 @@ #include #endif -#ifndef __has_feature -#define __has_feature(__x) 0 -#endif - -#if !__has_feature(cxx_exceptions) -# define _CXXBRIDGE03_NO_EXCEPTIONS -#endif -#if !__EXCEPTIONS -# define _CXXBRIDGE03_NO_EXCEPTIONS -#endif - -[[noreturn]] inline static void throw_out_of_range(const char *msg) { -#ifndef _CXXBRIDGE03_NO_EXCEPTIONS - throw std::out_of_range(msg); -#else - ((void)msg); - std::abort(); -#endif -} - namespace rust { inline namespace cxxbridge03 { @@ -303,6 +283,9 @@ using try_fn = TryFn; //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details +template +void panic [[noreturn]] (const char *msg); + template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); @@ -499,7 +482,7 @@ const T &Vec::operator[](size_t n) const noexcept { template const T &Vec::at(size_t n) const { if (n >= this->size()) - throw_out_of_range("Vec"); + panic("Vec"); return (*this)[n]; } diff --git a/src/cxx.cc b/src/cxx.cc index da05ee2..0fa55b2 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -6,16 +6,6 @@ #include #include -template -static void panic [[noreturn]] (const char *msg) { -#if defined(RUST_CXX_NO_EXCEPTIONS) - std::cerr << "Error: " << msg << ". Aborting." << std::endl; - std::terminate(); -#else - throw Exception(msg); -#endif -} - extern "C" { const char *cxxbridge03$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -42,6 +32,18 @@ bool cxxbridge03$str$valid(const char *ptr, size_t len) noexcept; namespace rust { inline namespace cxxbridge03 { +template +void panic [[noreturn]] (const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::cerr << "Error: " << msg << ". Aborting." << std::endl; + std::terminate(); +#else + throw Exception(msg); +#endif +} + +template void panic(const char *msg); + String::String() noexcept { cxxbridge03$string$new(this); } String::String(const String &other) noexcept { From 78c1e6b761bd7b3f43435a2e32f2d08f50f2e2d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 05:05:49 +0000 Subject: [PATCH 693/2232] Resolve 'Failed to specialize function template' MSVC error Before: src/cxx.cc(295): error C2893: Failed to specialize function template 'void rust::cxxbridge03::panic(const char *)' D:\a\cxx\cxx\src\../include/cxx.h(287): note: see declaration of 'rust::cxxbridge03::panic' src/cxx.cc(295): note: With the following template arguments: src/cxx.cc(295): note: 'Exception=std::out_of_range' --- diff --git a/src/cxx.cc b/src/cxx.cc index 0fa55b2..2d2349d 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -42,7 +42,7 @@ void panic [[noreturn]] (const char *msg) { #endif } -template void panic(const char *msg); +template void panic [[noreturn]] (const char *msg); String::String() noexcept { cxxbridge03$string$new(this); } From 001e6f0c7ce5e93b01cacf06ec750779b020ac04 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 05:13:22 +0000 Subject: [PATCH 694/2232] Merge pull request #262 from dtolnay/throw Unify the way that String construction and Vec indexing throw --- diff --git a/include/cxx.h b/include/cxx.h index 786dbb3..d3f6238 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -14,26 +14,6 @@ #include #endif -#ifndef __has_feature -#define __has_feature(__x) 0 -#endif - -#if !__has_feature(cxx_exceptions) -# define _CXXBRIDGE03_NO_EXCEPTIONS -#endif -#if !__EXCEPTIONS -# define _CXXBRIDGE03_NO_EXCEPTIONS -#endif - -[[noreturn]] inline static void throw_out_of_range(const char *msg) { -#ifndef _CXXBRIDGE03_NO_EXCEPTIONS - throw std::out_of_range(msg); -#else - ((void)msg); - std::abort(); -#endif -} - namespace rust { inline namespace cxxbridge03 { @@ -303,6 +283,9 @@ using try_fn = TryFn; //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details +template +void panic [[noreturn]] (const char *msg); + template Ret Fn::operator()(Args... args) const noexcept(!Throws) { return (*this->trampoline)(std::move(args)..., this->fn); @@ -499,7 +482,7 @@ const T &Vec::operator[](size_t n) const noexcept { template const T &Vec::at(size_t n) const { if (n >= this->size()) - throw_out_of_range("Vec"); + panic("Vec"); return (*this)[n]; } diff --git a/src/cxx.cc b/src/cxx.cc index da05ee2..2d2349d 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -6,16 +6,6 @@ #include #include -template -static void panic [[noreturn]] (const char *msg) { -#if defined(RUST_CXX_NO_EXCEPTIONS) - std::cerr << "Error: " << msg << ". Aborting." << std::endl; - std::terminate(); -#else - throw Exception(msg); -#endif -} - extern "C" { const char *cxxbridge03$cxx_string$data(const std::string &s) noexcept { return s.data(); @@ -42,6 +32,18 @@ bool cxxbridge03$str$valid(const char *ptr, size_t len) noexcept; namespace rust { inline namespace cxxbridge03 { +template +void panic [[noreturn]] (const char *msg) { +#if defined(RUST_CXX_NO_EXCEPTIONS) + std::cerr << "Error: " << msg << ". Aborting." << std::endl; + std::terminate(); +#else + throw Exception(msg); +#endif +} + +template void panic [[noreturn]] (const char *msg); + String::String() noexcept { cxxbridge03$string$new(this); } String::String(const String &other) noexcept { From 61adf428be27bc20200c87a977692a39219f1b5f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 05:13:31 +0000 Subject: [PATCH 695/2232] Add test for Vec::at exception message --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 66200e5..4ab407b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -209,7 +209,16 @@ void c_take_ref_vector(const std::vector &v) { void c_take_rust_vec(rust::Vec v) { c_take_ref_rust_vec(v); } -void c_take_rust_vec_index(rust::Vec v) { c_take_ref_rust_vec_index(v); } +void c_take_rust_vec_index(rust::Vec v) { + try { + v.at(100); + } catch (const std::out_of_range &ex) { + std::string expected = "Vec"; + if (ex.what() == expected) { + cxx_test_suite_set_correct(); + } + } +} void c_take_rust_vec_shared(rust::Vec v) { uint32_t sum = 0; From 8e1e6ac25cf899cf7030f11b6096b991ad261381 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 05:13:31 +0000 Subject: [PATCH 696/2232] Extend exception message from Vec::at --- diff --git a/include/cxx.h b/include/cxx.h index d3f6238..90c0524 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -481,8 +481,9 @@ const T &Vec::operator[](size_t n) const noexcept { template const T &Vec::at(size_t n) const { - if (n >= this->size()) - panic("Vec"); + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } return (*this)[n]; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4ab407b..682b9c9 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -213,7 +213,7 @@ void c_take_rust_vec_index(rust::Vec v) { try { v.at(100); } catch (const std::out_of_range &ex) { - std::string expected = "Vec"; + std::string expected = "rust::Vec index out of range"; if (ex.what() == expected) { cxx_test_suite_set_correct(); } From b10c4bc33a77bf17b94d6f1815383c686cebf9b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 05:13:31 +0000 Subject: [PATCH 697/2232] Format with clang-format 10 --- diff --git a/include/cxx.h b/include/cxx.h index 90c0524..9a9d5fa 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -494,7 +494,7 @@ const T &Vec::front() const { template const T &Vec::back() const { - return (*this)[this->size()-1]; + return (*this)[this->size() - 1]; } template @@ -522,14 +522,14 @@ Vec::const_iterator::operator++(int) noexcept { } template -bool Vec::const_iterator::operator==(const const_iterator &other) const - noexcept { +bool Vec::const_iterator::operator==( + const const_iterator &other) const noexcept { return this->pos == other.pos; } template -bool Vec::const_iterator::operator!=(const const_iterator &other) const - noexcept { +bool Vec::const_iterator::operator!=( + const const_iterator &other) const noexcept { return this->pos != other.pos; } diff --git a/src/cxx.cc b/src/cxx.cc index 2d2349d..b953904 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -42,7 +42,7 @@ void panic [[noreturn]] (const char *msg) { #endif } -template void panic [[noreturn]] (const char *msg); +template void panic[[noreturn]] (const char *msg); String::String() noexcept { cxxbridge03$string$new(this); } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 682b9c9..4cc771e 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -243,12 +243,8 @@ void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { } void c_take_rust_vec_shared_index(rust::Vec v) { - if (v[0].z == 1010 && - v.at(0).z == 1010 && - v.front().z == 1010 && - v[1].z == 1011 && - v.at(1).z == 1011 && - v.back().z == 1011) { + if (v[0].z == 1010 && v.at(0).z == 1010 && v.front().z == 1010 && + v[1].z == 1011 && v.at(1).z == 1011 && v.back().z == 1011) { cxx_test_suite_set_correct(); } } @@ -261,14 +257,8 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } void c_take_ref_rust_vec_index(const rust::Vec &v) { - if (v[0] == 86 && - v.at(0) == 86 && - v.front() == 86 && - v[1] == 75 && - v.at(1) == 75 && - v[3] == 9 && - v.at(3) == 9 && - v.back() == 9) { + if (v[0] == 86 && v.at(0) == 86 && v.front() == 86 && v[1] == 75 && + v.at(1) == 75 && v[3] == 9 && v.at(3) == 9 && v.back() == 9) { cxx_test_suite_set_correct(); } } From a7ba6a629ced417a20b1ba15d88e37d05409c704 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 07:15:55 +0000 Subject: [PATCH 698/2232] Format with rustfmt 1.4.20 --- diff --git a/tests/test.rs b/tests/test.rs index 55c28d2..9c71bd5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -109,13 +109,12 @@ fn test_c_take() { let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec(); check!(ffi::c_take_rust_vec(test_vec.clone())); check!(ffi::c_take_rust_vec_index(test_vec.clone())); - let shared_test_vec = vec![ - ffi::Shared { z: 1010 }, - ffi::Shared { z: 1011 } - ]; + let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }]; check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone())); check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone())); - check!(ffi::c_take_rust_vec_shared_forward_iterator(shared_test_vec)); + check!(ffi::c_take_rust_vec_shared_forward_iterator( + shared_test_vec, + )); check!(ffi::c_take_ref_rust_vec(&test_vec)); check!(ffi::c_take_ref_rust_vec_index(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); From f1c7f3219be65b9afe247401a75ca35ca6dca075 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 07:58:15 +0000 Subject: [PATCH 699/2232] Handle &mut reference in more places --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 431fd3b..ab1206c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -256,10 +256,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - quote!(::cxx::private::RustString::from_ref(#var)) - } - Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => quote!(::cxx::private::RustString::from_ref(#var)), + Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), + }, + Type::RustVec(_) => match ty.mutability { + None => quote!(::cxx::private::RustVec::from_ref(#var)), + Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)), + }, _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -331,10 +335,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - Some(quote!(#call.map(|r| r.as_string()))) - } - Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_string()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_vec()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec()))), + }, _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), @@ -348,8 +356,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::RustVec(_) => Some(quote!(#call.into_vec())), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), - Type::RustVec(_) => Some(quote!(#call.as_vec())), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(#call.as_string())), + Some(_) => Some(quote!(#call.as_mut_string())), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(#call.as_vec())), + Some(_) => Some(quote!(#call.as_mut_vec())), + }, _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), @@ -497,8 +511,14 @@ fn expand_rust_function_shim_impl( Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(i) if i == RustString => quote!(#ident.as_string()), - Type::RustVec(_) => quote!(#ident.as_vec()), + Type::Ident(i) if i == RustString => match ty.mutability { + None => quote!(#ident.as_string()), + Some(_) => quote!(#ident.as_mut_string()), + }, + Type::RustVec(_) => match ty.mutability { + None => quote!(#ident.as_vec()), + Some(_) => quote!(#ident.as_mut_vec()), + }, _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), @@ -532,10 +552,14 @@ fn expand_rust_function_shim_impl( Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - Some(quote!(::cxx::private::RustString::from_ref(#call))) - } - Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustString::from_ref(#call))), + Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), + }, _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), @@ -860,14 +884,19 @@ fn expand_extern_type(ty: &Type) -> TokenStream { let elem = expand_extern_type(&ty.inner); quote!(::cxx::private::RustVec<#elem>) } - Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), - Type::RustVec(ty) => { - let inner = expand_extern_type(&ty.inner); - quote!(&::cxx::private::RustVec<#inner>) + Type::Ref(ty) => { + let mutability = ty.mutability; + match &ty.inner { + Type::Ident(ident) if ident == RustString => { + quote!(&#mutability ::cxx::private::RustString) + } + Type::RustVec(ty) => { + let inner = expand_extern_type(&ty.inner); + quote!(&#mutability ::cxx::private::RustVec<#inner>) + } + _ => quote!(#ty), } - _ => quote!(#ty), - }, + } Type::Str(_) => quote!(::cxx::private::RustStr), Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8), _ => quote!(#ty), diff --git a/src/rust_string.rs b/src/rust_string.rs index a923ced..f0d1df1 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -14,6 +14,10 @@ impl RustString { unsafe { &*(s as *const String as *const RustString) } } + pub fn from_mut(s: &mut String) -> &mut Self { + unsafe { &mut *(s as *mut String as *mut RustString) } + } + pub fn into_string(self) -> String { self.repr } diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 4c5035d..9ff4bbf 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -16,6 +16,10 @@ impl RustVec { unsafe { &*(v as *const Vec as *const RustVec) } } + pub fn from_mut(v: &mut Vec) -> &mut Self { + unsafe { &mut *(v as *mut Vec as *mut RustVec) } + } + pub fn into_vec(self) -> Vec { self.repr } From 18d93b6d5850c54bd64b08f128018de9d919826a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 08:00:18 +0000 Subject: [PATCH 700/2232] Add &mut tests --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e491343..d69163a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -32,6 +32,7 @@ pub mod ffi { fn c_return_box() -> Box; fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; + fn c_return_mut(shared: &mut Shared) -> &mut usize; fn c_return_str(shared: &Shared) -> &str; fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; @@ -41,8 +42,10 @@ pub mod ffi { fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; + fn c_return_mut_vector(c: &mut C) -> &mut CxxVector; fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; + fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -113,11 +116,13 @@ pub mod ffi { fn r_return_box() -> Box; fn r_return_unique_ptr() -> UniquePtr; fn r_return_ref(shared: &Shared) -> &usize; + fn r_return_mut(shared: &mut Shared) -> &mut usize; fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; + fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_enum(n: u32) -> Enum; @@ -195,6 +200,10 @@ fn r_return_ref(shared: &ffi::Shared) -> &usize { &shared.z } +fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize { + &mut shared.z +} + fn r_return_str(shared: &ffi::Shared) -> &str { let _ = shared; "2020" @@ -220,6 +229,11 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { unimplemented!() } +fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec { + let _ = shared; + unimplemented!() +} + fn r_return_identity(n: usize) -> usize { n } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4cc771e..b213930 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -35,6 +35,8 @@ size_t C::get_fail() { throw std::runtime_error("unimplemented"); } const std::vector &C::get_v() const { return this->v; } +std::vector &C::get_v() { return this->v; } + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -49,6 +51,8 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } +size_t &c_return_mut(Shared &shared) { return shared.z; } + rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; @@ -99,6 +103,8 @@ const std::vector &c_return_ref_vector(const C &c) { return c.get_v(); } +std::vector &c_return_mut_vector(C &c) { return c.get_v(); } + rust::Vec c_return_rust_vec() { throw std::runtime_error("unimplemented"); } @@ -108,6 +114,11 @@ const rust::Vec &c_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } +rust::Vec &c_return_mut_rust_vec(C &c) { + (void)c; + throw std::runtime_error("unimplemented"); +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7b7d273..fe644eb 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,6 +19,7 @@ public: size_t set_succeed(size_t n); size_t get_fail(); const std::vector &get_v() const; + std::vector &get_v(); private: size_t n; @@ -35,6 +36,7 @@ Shared c_return_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); +size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); @@ -44,8 +46,10 @@ std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); +std::vector &c_return_mut_vector(C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); +rust::Vec &c_return_mut_rust_vec(C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); From adf67ae1e5612d8068699fc1855c102b2ec34134 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 08:09:17 +0000 Subject: [PATCH 701/2232] Merge pull request #263 from dtolnay/mut Handle &mut reference in more places --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 431fd3b..ab1206c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -256,10 +256,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - quote!(::cxx::private::RustString::from_ref(#var)) - } - Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => quote!(::cxx::private::RustString::from_ref(#var)), + Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), + }, + Type::RustVec(_) => match ty.mutability { + None => quote!(::cxx::private::RustVec::from_ref(#var)), + Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)), + }, _ => quote!(#var), }, Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), @@ -331,10 +335,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - Some(quote!(#call.map(|r| r.as_string()))) - } - Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_string()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_vec()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec()))), + }, _ => None, }, Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))), @@ -348,8 +356,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Type::RustVec(_) => Some(quote!(#call.into_vec())), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())), - Type::RustVec(_) => Some(quote!(#call.as_vec())), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(#call.as_string())), + Some(_) => Some(quote!(#call.as_mut_string())), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(#call.as_vec())), + Some(_) => Some(quote!(#call.as_mut_vec())), + }, _ => None, }, Type::Str(_) => Some(quote!(#call.as_str())), @@ -497,8 +511,14 @@ fn expand_rust_function_shim_impl( Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(i) if i == RustString => quote!(#ident.as_string()), - Type::RustVec(_) => quote!(#ident.as_vec()), + Type::Ident(i) if i == RustString => match ty.mutability { + None => quote!(#ident.as_string()), + Some(_) => quote!(#ident.as_mut_string()), + }, + Type::RustVec(_) => match ty.mutability { + None => quote!(#ident.as_vec()), + Some(_) => quote!(#ident.as_mut_vec()), + }, _ => quote!(#ident), }, Type::Str(_) => quote!(#ident.as_str()), @@ -532,10 +552,14 @@ fn expand_rust_function_shim_impl( Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => { - Some(quote!(::cxx::private::RustString::from_ref(#call))) - } - Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))), + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustString::from_ref(#call))), + Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), + }, _ => None, }, Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), @@ -860,14 +884,19 @@ fn expand_extern_type(ty: &Type) -> TokenStream { let elem = expand_extern_type(&ty.inner); quote!(::cxx::private::RustVec<#elem>) } - Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString), - Type::RustVec(ty) => { - let inner = expand_extern_type(&ty.inner); - quote!(&::cxx::private::RustVec<#inner>) + Type::Ref(ty) => { + let mutability = ty.mutability; + match &ty.inner { + Type::Ident(ident) if ident == RustString => { + quote!(&#mutability ::cxx::private::RustString) + } + Type::RustVec(ty) => { + let inner = expand_extern_type(&ty.inner); + quote!(&#mutability ::cxx::private::RustVec<#inner>) + } + _ => quote!(#ty), } - _ => quote!(#ty), - }, + } Type::Str(_) => quote!(::cxx::private::RustStr), Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8), _ => quote!(#ty), diff --git a/src/rust_string.rs b/src/rust_string.rs index a923ced..f0d1df1 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -14,6 +14,10 @@ impl RustString { unsafe { &*(s as *const String as *const RustString) } } + pub fn from_mut(s: &mut String) -> &mut Self { + unsafe { &mut *(s as *mut String as *mut RustString) } + } + pub fn into_string(self) -> String { self.repr } diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 4c5035d..9ff4bbf 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -16,6 +16,10 @@ impl RustVec { unsafe { &*(v as *const Vec as *const RustVec) } } + pub fn from_mut(v: &mut Vec) -> &mut Self { + unsafe { &mut *(v as *mut Vec as *mut RustVec) } + } + pub fn into_vec(self) -> Vec { self.repr } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e491343..d69163a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -32,6 +32,7 @@ pub mod ffi { fn c_return_box() -> Box; fn c_return_unique_ptr() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; + fn c_return_mut(shared: &mut Shared) -> &mut usize; fn c_return_str(shared: &Shared) -> &str; fn c_return_sliceu8(shared: &Shared) -> &[u8]; fn c_return_rust_string() -> String; @@ -41,8 +42,10 @@ pub mod ffi { fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; + fn c_return_mut_vector(c: &mut C) -> &mut CxxVector; fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; + fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -113,11 +116,13 @@ pub mod ffi { fn r_return_box() -> Box; fn r_return_unique_ptr() -> UniquePtr; fn r_return_ref(shared: &Shared) -> &usize; + fn r_return_mut(shared: &mut Shared) -> &mut usize; fn r_return_str(shared: &Shared) -> &str; fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; + fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec; fn r_return_identity(_: usize) -> usize; fn r_return_sum(_: usize, _: usize) -> usize; fn r_return_enum(n: u32) -> Enum; @@ -195,6 +200,10 @@ fn r_return_ref(shared: &ffi::Shared) -> &usize { &shared.z } +fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize { + &mut shared.z +} + fn r_return_str(shared: &ffi::Shared) -> &str { let _ = shared; "2020" @@ -220,6 +229,11 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { unimplemented!() } +fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec { + let _ = shared; + unimplemented!() +} + fn r_return_identity(n: usize) -> usize { n } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4cc771e..b213930 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -35,6 +35,8 @@ size_t C::get_fail() { throw std::runtime_error("unimplemented"); } const std::vector &C::get_v() const { return this->v; } +std::vector &C::get_v() { return this->v; } + size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } @@ -49,6 +51,8 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } +size_t &c_return_mut(Shared &shared) { return shared.z; } + rust::Str c_return_str(const Shared &shared) { (void)shared; return "2020"; @@ -99,6 +103,8 @@ const std::vector &c_return_ref_vector(const C &c) { return c.get_v(); } +std::vector &c_return_mut_vector(C &c) { return c.get_v(); } + rust::Vec c_return_rust_vec() { throw std::runtime_error("unimplemented"); } @@ -108,6 +114,11 @@ const rust::Vec &c_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } +rust::Vec &c_return_mut_rust_vec(C &c) { + (void)c; + throw std::runtime_error("unimplemented"); +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7b7d273..fe644eb 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,6 +19,7 @@ public: size_t set_succeed(size_t n); size_t get_fail(); const std::vector &get_v() const; + std::vector &get_v(); private: size_t n; @@ -35,6 +36,7 @@ Shared c_return_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); +size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); @@ -44,8 +46,10 @@ std::unique_ptr> c_return_unique_ptr_vector_f64(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); +std::vector &c_return_mut_vector(C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); +rust::Vec &c_return_mut_rust_vec(C &c); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); From f7a592bfcfdf645c36ec9dbe2a6b1e701864a8eb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 27 2020 08:14:08 +0000 Subject: [PATCH 702/2232] Release 0.3.5 --- diff --git a/Cargo.toml b/Cargo.toml index 234b098..1c59ccb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.4" # remember to update html_root_url +version = "0.3.5" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -19,14 +19,14 @@ default = [] # c++11 "c++17" = [] [dependencies] -cxxbridge-macro = { version = "=0.3.4", path = "macro" } +cxxbridge-macro = { version = "=0.3.5", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.4", path = "gen/build" } +cxx-build = { version = "=0.3.5", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index b43a37e..b242383 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.4" +version = "0.3.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index b9d4b78..c0cfeb4 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.4" +version = "0.3.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 21c4318..1b85847 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.4" +version = "0.3.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 91e0098..69e45b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.4")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.5")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 10c577d..aea33a4 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.4" +version = "0.3.5" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.4" +version = "0.3.5" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.4" +version = "0.3.5" dependencies = [ "anyhow", "clap", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.4" +version = "0.3.5" dependencies = [ "cxx", "proc-macro2", From 57003b0e78e240315fa12742fd42a252d3da7206 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 05:57:13 +0000 Subject: [PATCH 703/2232] Inline writing include guard --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 51c4b02..1196837 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -314,7 +314,16 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - write_include_guard_start(out, &strct.ident); + writeln!( + out, + "#ifndef CXXBRIDGE03_STRUCT_{}{}", + out.namespace, strct.ident, + ); + writeln!( + out, + "#define CXXBRIDGE03_STRUCT_{}{}", + out.namespace, strct.ident, + ); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -325,7 +334,11 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "{};", field.ident); } writeln!(out, "}};"); - write_include_guard_end(out, &strct.ident); + writeln!( + out, + "#endif // CXXBRIDGE03_STRUCT_{}{}", + out.namespace, strct.ident, + ); } fn write_struct_decl(out: &mut OutFile, ident: &Ident) { @@ -336,31 +349,17 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { writeln!(out, "using {} = {};", ident, ident); } -const INCLUDE_GUARD_PREFIX: &'static str = "CXXBRIDGE03_TYPE_"; - -fn write_include_guard_start(out: &mut OutFile, ident: &Ident) { - writeln!( - out, - "#ifndef {}{}{}", - INCLUDE_GUARD_PREFIX, out.namespace, ident - ); +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { writeln!( out, - "#define {}{}{}", - INCLUDE_GUARD_PREFIX, out.namespace, ident + "#ifndef CXXBRIDGE03_STRUCT_{}{}", + out.namespace, ety.ident, ); -} - -fn write_include_guard_end(out: &mut OutFile, ident: &Ident) { writeln!( out, - "#endif // {}{}{}", - INCLUDE_GUARD_PREFIX, out.namespace, ident + "#define CXXBRIDGE03_STRUCT_{}{}", + out.namespace, ety.ident, ); -} - -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - write_include_guard_start(out, &ety.ident); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -375,11 +374,24 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, ";"); } writeln!(out, "}};"); - write_include_guard_end(out, &ety.ident); + writeln!( + out, + "#endif // CXXBRIDGE03_STRUCT_{}{}", + out.namespace, ety.ident, + ); } fn write_enum(out: &mut OutFile, enm: &Enum) { - write_include_guard_start(out, &enm.ident); + writeln!( + out, + "#ifndef CXXBRIDGE03_ENUM_{}{}", + out.namespace, enm.ident, + ); + writeln!( + out, + "#define CXXBRIDGE03_ENUM_{}{}", + out.namespace, enm.ident, + ); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -390,7 +402,11 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); - write_include_guard_end(out, &enm.ident); + writeln!( + out, + "#endif // CXXBRIDGE03_ENUM_{}{}", + out.namespace, enm.ident, + ); } fn check_enum(out: &mut OutFile, enm: &Enum) { From a25ea9cc04b7bc8b4d2d5ae894d0eb2d9979660e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 05:59:38 +0000 Subject: [PATCH 704/2232] Write guards in a way that rustfmt formats tighter vertically --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 1196837..a9b8dde 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -314,16 +314,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - writeln!( - out, - "#ifndef CXXBRIDGE03_STRUCT_{}{}", - out.namespace, strct.ident, - ); - writeln!( - out, - "#define CXXBRIDGE03_STRUCT_{}{}", - out.namespace, strct.ident, - ); + let guard = format!("CXXBRIDGE03_STRUCT_{}{}", out.namespace, strct.ident); + writeln!(out, "#ifndef {}", guard); + writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -334,11 +327,7 @@ fn write_struct(out: &mut OutFile, strct: &Struct) { writeln!(out, "{};", field.ident); } writeln!(out, "}};"); - writeln!( - out, - "#endif // CXXBRIDGE03_STRUCT_{}{}", - out.namespace, strct.ident, - ); + writeln!(out, "#endif // {}", guard); } fn write_struct_decl(out: &mut OutFile, ident: &Ident) { @@ -350,16 +339,9 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - writeln!( - out, - "#ifndef CXXBRIDGE03_STRUCT_{}{}", - out.namespace, ety.ident, - ); - writeln!( - out, - "#define CXXBRIDGE03_STRUCT_{}{}", - out.namespace, ety.ident, - ); + let guard = format!("CXXBRIDGE03_STRUCT_{}{}", out.namespace, ety.ident); + writeln!(out, "#ifndef {}", guard); + writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -374,24 +356,13 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, ";"); } writeln!(out, "}};"); - writeln!( - out, - "#endif // CXXBRIDGE03_STRUCT_{}{}", - out.namespace, ety.ident, - ); + writeln!(out, "#endif // {}", guard); } fn write_enum(out: &mut OutFile, enm: &Enum) { - writeln!( - out, - "#ifndef CXXBRIDGE03_ENUM_{}{}", - out.namespace, enm.ident, - ); - writeln!( - out, - "#define CXXBRIDGE03_ENUM_{}{}", - out.namespace, enm.ident, - ); + let guard = format!("CXXBRIDGE03_ENUM_{}{}", out.namespace, enm.ident); + writeln!(out, "#ifndef {}", guard); + writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -402,11 +373,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, " {} = {},", variant.ident, variant.discriminant); } writeln!(out, "}};"); - writeln!( - out, - "#endif // CXXBRIDGE03_ENUM_{}{}", - out.namespace, enm.ident, - ); + writeln!(out, "#endif // {}", guard); } fn check_enum(out: &mut OutFile, enm: &Enum) { From 3cb7542aec0ae4522af80c18730309d98d25d5b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 06:41:13 +0000 Subject: [PATCH 705/2232] Fix link where intra-rustdoc link is not kicking in From looking at the docs.rs pages for 0.3.1 through 0.3.4, it looks like this link used to render correctly, but doesn't anymore starting with 0.3.5... Unclear whether caused by a rustdoc change or a cxx change. --- diff --git a/src/lib.rs b/src/lib.rs index 69e45b3..bbe8788 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -398,6 +398,8 @@ pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; /// For use in impls of the `ExternType` trait. See [`ExternType`]. +/// +/// [`ExternType`]: trait.ExternType.html pub use cxxbridge_macro::type_id; // Not public API. From f2eb3e70572be2f3880a6fd7529a950007ecc7bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 06:51:23 +0000 Subject: [PATCH 706/2232] Add cxx::String and cxx::Vector aliases --- diff --git a/src/lib.rs b/src/lib.rs index bbe8788..5210940 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -402,6 +402,20 @@ pub use cxxbridge_macro::bridge; /// [`ExternType`]: trait.ExternType.html pub use cxxbridge_macro::type_id; +/// Synonym for `CxxString`. +/// +/// To avoid confusion with Rust's standard library string you probably +/// shouldn't import this type with `use`. Instead, write `cxx::String`, or +/// import and use `CxxString`. +pub type String = CxxString; + +/// Synonym for `CxxVector`. +/// +/// To avoid confusion with Rust's standard library vector you probably +/// shouldn't import this type with `use`. Instead, write `cxx::Vector`, or +/// import and use `CxxVector`. +pub type Vector = CxxVector; + // Not public API. #[doc(hidden)] pub mod private { From b69cb0a7d99412aac5196674bf5fce03d1a36f0d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 06:57:10 +0000 Subject: [PATCH 707/2232] Merge pull request #265 from dtolnay/alias Add cxx::String and cxx::Vector aliases --- diff --git a/src/lib.rs b/src/lib.rs index bbe8788..5210940 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -402,6 +402,20 @@ pub use cxxbridge_macro::bridge; /// [`ExternType`]: trait.ExternType.html pub use cxxbridge_macro::type_id; +/// Synonym for `CxxString`. +/// +/// To avoid confusion with Rust's standard library string you probably +/// shouldn't import this type with `use`. Instead, write `cxx::String`, or +/// import and use `CxxString`. +pub type String = CxxString; + +/// Synonym for `CxxVector`. +/// +/// To avoid confusion with Rust's standard library vector you probably +/// shouldn't import this type with `use`. Instead, write `cxx::Vector`, or +/// import and use `CxxVector`. +pub type Vector = CxxVector; + // Not public API. #[doc(hidden)] pub mod private { From 33f56ad8d5e536d3a42f3f2afa74d341d7b0255d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 07:25:41 +0000 Subject: [PATCH 708/2232] Implement Vec --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ab1206c..0869c2c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -260,6 +260,10 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types None => quote!(::cxx::private::RustString::from_ref(#var)), Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)), + Some(_) => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)), + }, Type::RustVec(_) => match ty.mutability { None => quote!(::cxx::private::RustVec::from_ref(#var)), Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)), @@ -332,13 +336,23 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), - Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(#call.map(|r| r.into_vec_string()))) + } else { + Some(quote!(#call.map(|r| r.into_vec()))) + } + } Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_string()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_vec_string()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec_string()))), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_vec()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec()))), @@ -353,13 +367,23 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), - Type::RustVec(_) => Some(quote!(#call.into_vec())), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(#call.into_vec_string())) + } else { + Some(quote!(#call.into_vec())) + } + } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(#call.as_string())), Some(_) => Some(quote!(#call.as_mut_string())), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(#call.as_vec_string())), + Some(_) => Some(quote!(#call.as_mut_vec_string())), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(#call.as_vec())), Some(_) => Some(quote!(#call.as_mut_vec())), @@ -508,13 +532,23 @@ fn expand_rust_function_shim_impl( quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), - Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), + Type::RustVec(vec) => { + if vec.inner == RustString { + quote!(::std::mem::take((*#ident).as_mut_vec_string())) + } else { + quote!(::std::mem::take((*#ident).as_mut_vec())) + } + } Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i == RustString => match ty.mutability { None => quote!(#ident.as_string()), Some(_) => quote!(#ident.as_mut_string()), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => quote!(#ident.as_vec_string()), + Some(_) => quote!(#ident.as_mut_vec_string()), + }, Type::RustVec(_) => match ty.mutability { None => quote!(#ident.as_vec()), Some(_) => quote!(#ident.as_mut_vec()), @@ -549,13 +583,23 @@ fn expand_rust_function_shim_impl( Some(quote!(::cxx::private::RustString::from(#call))) } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), - Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(::cxx::private::RustVec::from_vec_string(#call))) + } else { + Some(quote!(::cxx::private::RustVec::from(#call))) + } + } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(::cxx::private::RustString::from_ref(#call))), Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string(#call))), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string(#call))), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), diff --git a/src/cxx.cc b/src/cxx.cc index b953904..e00fd6a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -280,7 +280,8 @@ void cxxbridge03$unique_ptr$std$string$drop( #define FOR_EACH_RUST_VEC(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ - MACRO(bool, bool) + MACRO(bool, bool) \ + MACRO(string, rust::String) extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 9ff4bbf..5e7082a 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,3 +1,6 @@ +use crate::rust_string::RustString; +use std::mem::ManuallyDrop; + #[repr(C)] pub struct RustVec { repr: Vec, @@ -40,3 +43,37 @@ impl RustVec { self.repr.as_ptr() } } + +impl RustVec { + pub fn from_vec_string(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); + let ptr = v.as_mut_ptr().cast::(); + let len = v.len(); + let cap = v.capacity(); + Self::from(unsafe { Vec::from_raw_parts(ptr, len, cap) }) + } + + pub fn from_ref_vec_string(v: &Vec) -> &Self { + Self::from_ref(unsafe { &*(v as *const Vec as *const Vec) }) + } + + pub fn from_mut_vec_string(v: &mut Vec) -> &mut Self { + Self::from_mut(unsafe { &mut *(v as *mut Vec as *mut Vec) }) + } + + pub fn into_vec_string(self) -> Vec { + let mut v = ManuallyDrop::new(self.repr); + let ptr = v.as_mut_ptr().cast::(); + let len = v.len(); + let cap = v.capacity(); + unsafe { Vec::from_raw_parts(ptr, len, cap) } + } + + pub fn as_vec_string(&self) -> &Vec { + unsafe { &*(&self.repr as *const Vec as *const Vec) } + } + + pub fn as_mut_vec_string(&mut self) -> &mut Vec { + unsafe { &mut *(&mut self.repr as *mut Vec as *mut Vec) } + } +} diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 63d4ba7..d8e0f4a 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -3,6 +3,11 @@ use std::ptr; use std::slice; use std::str; +#[repr(C)] +pub(crate) struct RustString { + repr: String, +} + #[export_name = "cxxbridge03$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 9ce87ab..5465471 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,8 +1,9 @@ +use super::rust_string::RustString; use std::mem; use std::ptr; #[repr(C)] -pub struct RustVec { +pub(crate) struct RustVec { repr: Vec, } @@ -13,38 +14,38 @@ macro_rules! attr { }; } -macro_rules! rust_vec_shims_for_primitive { - ($ty:ident) => { +macro_rules! rust_vec_shims { + ($segment:expr, $ty:ty) => { const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); const_assert_eq!(mem::align_of::(), mem::align_of::>()); const _: () = { attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { ptr::write(this, RustVec { repr: Vec::new() }); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { (*this).repr.len() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { (*this).repr.as_ptr() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$stride")] unsafe extern "C" fn __stride() -> usize { mem::size_of::<$ty>() } @@ -53,6 +54,12 @@ macro_rules! rust_vec_shims_for_primitive { }; } +macro_rules! rust_vec_shims_for_primitive { + ($ty:ident) => { + rust_vec_shims!(stringify!($ty), $ty); + }; +} + rust_vec_shims_for_primitive!(bool); rust_vec_shims_for_primitive!(u8); rust_vec_shims_for_primitive!(u16); @@ -64,3 +71,5 @@ rust_vec_shims_for_primitive!(i32); rust_vec_shims_for_primitive!(i64); rust_vec_shims_for_primitive!(f32); rust_vec_shims_for_primitive!(f64); + +rust_vec_shims!("string", RustString); diff --git a/syntax/check.rs b/syntax/check.rs index c54430c..71fab3b 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -92,8 +92,9 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { match Atom::from(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) => return, - Some(Bool) | Some(RustString) => { /* todo */ } + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) + | Some(RustString) => return, + Some(Bool) => { /* todo */ } Some(CxxString) => {} } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d69163a..cb688f3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -46,6 +46,7 @@ pub mod ffi { fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec; + fn c_return_rust_vec_string() -> Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -65,10 +66,12 @@ pub mod ffi { fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); fn c_take_rust_vec_shared(v: Vec); + fn c_take_rust_vec_string(v: Vec); fn c_take_rust_vec_index(v: Vec); fn c_take_rust_vec_shared_index(v: Vec); fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); + fn c_take_ref_rust_vec_string(v: &Vec); fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); /* @@ -87,6 +90,7 @@ pub mod ffi { fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; fn c_try_return_rust_vec() -> Result>; + fn c_try_return_rust_vec_string() -> Result>; fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec>; fn get(self: &C) -> usize; @@ -121,6 +125,7 @@ pub mod ffi { fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; + fn r_return_rust_vec_string() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec; fn r_return_identity(_: usize) -> usize; @@ -138,7 +143,9 @@ pub mod ffi { fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); fn r_take_rust_vec(v: Vec); + fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); + fn r_take_ref_rust_vec_string(v: &Vec); fn r_take_enum(e: Enum); fn r_try_return_void() -> Result<()>; @@ -224,6 +231,10 @@ fn r_return_rust_vec() -> Vec { Vec::new() } +fn r_return_rust_vec_string() -> Vec { + Vec::new() +} + fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { let _ = shared; unimplemented!() @@ -297,10 +308,18 @@ fn r_take_rust_vec(v: Vec) { let _ = v; } +fn r_take_rust_vec_string(v: Vec) { + let _ = v; +} + fn r_take_ref_rust_vec(v: &Vec) { let _ = v; } +fn r_take_ref_rust_vec_string(v: &Vec) { + let _ = v; +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b213930..b45e8ae 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -119,6 +119,10 @@ rust::Vec &c_return_mut_rust_vec(C &c) { throw std::runtime_error("unimplemented"); } +rust::Vec c_return_rust_vec_string() { + throw std::runtime_error("unimplemented"); +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } @@ -241,6 +245,11 @@ void c_take_rust_vec_shared(rust::Vec v) { } } +void c_take_rust_vec_string(rust::Vec v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { // Exercise requirements of ForwardIterator // https://en.cppreference.com/w/cpp/named_req/ForwardIterator @@ -267,6 +276,11 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } +void c_take_ref_rust_vec_string(const rust::Vec &v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_ref_rust_vec_index(const rust::Vec &v) { if (v[0] == 86 && v.at(0) == 86 && v.front() == 86 && v[1] == 75 && v.at(1) == 75 && v[3] == 9 && v.at(3) == 9 && v.back() == 9) { @@ -323,6 +337,10 @@ rust::Vec c_try_return_rust_vec() { throw std::runtime_error("unimplemented"); } +rust::Vec c_try_return_rust_vec_string() { + throw std::runtime_error("unimplemented"); +} + const rust::Vec &c_try_return_ref_rust_vec(const C &c) { (void)c; throw std::runtime_error("unimplemented"); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index fe644eb..0efbd62 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -50,6 +50,7 @@ std::vector &c_return_mut_vector(C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); rust::Vec &c_return_mut_rust_vec(C &c); +rust::Vec c_return_rust_vec_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); @@ -71,9 +72,11 @@ void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_string(rust::Vec v); void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); +void c_take_ref_rust_vec_string(const rust::Vec &v); void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); /* @@ -92,6 +95,7 @@ rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); rust::Vec c_try_return_rust_vec(); +rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); } // namespace tests From 13e4d3988cc681cbf06a582399946b37b9de882b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 07:33:13 +0000 Subject: [PATCH 709/2232] Merge pull request #266 from dtolnay/vec Implement Vec --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ab1206c..0869c2c 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -260,6 +260,10 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types None => quote!(::cxx::private::RustString::from_ref(#var)), Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)), + Some(_) => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)), + }, Type::RustVec(_) => match ty.mutability { None => quote!(::cxx::private::RustVec::from_ref(#var)), Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)), @@ -332,13 +336,23 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), - Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(#call.map(|r| r.into_vec_string()))) + } else { + Some(quote!(#call.map(|r| r.into_vec()))) + } + } Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_string()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(#call.map(|r| r.as_vec_string()))), + Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec_string()))), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_vec()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_vec()))), @@ -353,13 +367,23 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types efn.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), - Type::RustVec(_) => Some(quote!(#call.into_vec())), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(#call.into_vec_string())) + } else { + Some(quote!(#call.into_vec())) + } + } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(#call.as_string())), Some(_) => Some(quote!(#call.as_mut_string())), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(#call.as_vec_string())), + Some(_) => Some(quote!(#call.as_mut_vec_string())), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(#call.as_vec())), Some(_) => Some(quote!(#call.as_mut_vec())), @@ -508,13 +532,23 @@ fn expand_rust_function_shim_impl( quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), - Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())), + Type::RustVec(vec) => { + if vec.inner == RustString { + quote!(::std::mem::take((*#ident).as_mut_vec_string())) + } else { + quote!(::std::mem::take((*#ident).as_mut_vec())) + } + } Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i == RustString => match ty.mutability { None => quote!(#ident.as_string()), Some(_) => quote!(#ident.as_mut_string()), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => quote!(#ident.as_vec_string()), + Some(_) => quote!(#ident.as_mut_vec_string()), + }, Type::RustVec(_) => match ty.mutability { None => quote!(#ident.as_vec()), Some(_) => quote!(#ident.as_mut_vec()), @@ -549,13 +583,23 @@ fn expand_rust_function_shim_impl( Some(quote!(::cxx::private::RustString::from(#call))) } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), - Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(::cxx::private::RustVec::from_vec_string(#call))) + } else { + Some(quote!(::cxx::private::RustVec::from(#call))) + } + } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident == RustString => match ty.mutability { None => Some(quote!(::cxx::private::RustString::from_ref(#call))), Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string(#call))), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string(#call))), + }, Type::RustVec(_) => match ty.mutability { None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), diff --git a/src/cxx.cc b/src/cxx.cc index b953904..e00fd6a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -280,7 +280,8 @@ void cxxbridge03$unique_ptr$std$string$drop( #define FOR_EACH_RUST_VEC(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ - MACRO(bool, bool) + MACRO(bool, bool) \ + MACRO(string, rust::String) extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 9ff4bbf..5e7082a 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,3 +1,6 @@ +use crate::rust_string::RustString; +use std::mem::ManuallyDrop; + #[repr(C)] pub struct RustVec { repr: Vec, @@ -40,3 +43,37 @@ impl RustVec { self.repr.as_ptr() } } + +impl RustVec { + pub fn from_vec_string(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); + let ptr = v.as_mut_ptr().cast::(); + let len = v.len(); + let cap = v.capacity(); + Self::from(unsafe { Vec::from_raw_parts(ptr, len, cap) }) + } + + pub fn from_ref_vec_string(v: &Vec) -> &Self { + Self::from_ref(unsafe { &*(v as *const Vec as *const Vec) }) + } + + pub fn from_mut_vec_string(v: &mut Vec) -> &mut Self { + Self::from_mut(unsafe { &mut *(v as *mut Vec as *mut Vec) }) + } + + pub fn into_vec_string(self) -> Vec { + let mut v = ManuallyDrop::new(self.repr); + let ptr = v.as_mut_ptr().cast::(); + let len = v.len(); + let cap = v.capacity(); + unsafe { Vec::from_raw_parts(ptr, len, cap) } + } + + pub fn as_vec_string(&self) -> &Vec { + unsafe { &*(&self.repr as *const Vec as *const Vec) } + } + + pub fn as_mut_vec_string(&mut self) -> &mut Vec { + unsafe { &mut *(&mut self.repr as *mut Vec as *mut Vec) } + } +} diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 63d4ba7..d8e0f4a 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -3,6 +3,11 @@ use std::ptr; use std::slice; use std::str; +#[repr(C)] +pub(crate) struct RustString { + repr: String, +} + #[export_name = "cxxbridge03$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 9ce87ab..5465471 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,8 +1,9 @@ +use super::rust_string::RustString; use std::mem; use std::ptr; #[repr(C)] -pub struct RustVec { +pub(crate) struct RustVec { repr: Vec, } @@ -13,38 +14,38 @@ macro_rules! attr { }; } -macro_rules! rust_vec_shims_for_primitive { - ($ty:ident) => { +macro_rules! rust_vec_shims { + ($segment:expr, $ty:ty) => { const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); const_assert_eq!(mem::align_of::(), mem::align_of::>()); const _: () = { attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { ptr::write(this, RustVec { repr: Vec::new() }); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { (*this).repr.len() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { (*this).repr.as_ptr() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")] + #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$stride")] unsafe extern "C" fn __stride() -> usize { mem::size_of::<$ty>() } @@ -53,6 +54,12 @@ macro_rules! rust_vec_shims_for_primitive { }; } +macro_rules! rust_vec_shims_for_primitive { + ($ty:ident) => { + rust_vec_shims!(stringify!($ty), $ty); + }; +} + rust_vec_shims_for_primitive!(bool); rust_vec_shims_for_primitive!(u8); rust_vec_shims_for_primitive!(u16); @@ -64,3 +71,5 @@ rust_vec_shims_for_primitive!(i32); rust_vec_shims_for_primitive!(i64); rust_vec_shims_for_primitive!(f32); rust_vec_shims_for_primitive!(f64); + +rust_vec_shims!("string", RustString); diff --git a/syntax/check.rs b/syntax/check.rs index c54430c..71fab3b 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -92,8 +92,9 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { match Atom::from(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) => return, - Some(Bool) | Some(RustString) => { /* todo */ } + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) + | Some(RustString) => return, + Some(Bool) => { /* todo */ } Some(CxxString) => {} } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d69163a..cb688f3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -46,6 +46,7 @@ pub mod ffi { fn c_return_rust_vec() -> Vec; fn c_return_ref_rust_vec(c: &C) -> &Vec; fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec; + fn c_return_rust_vec_string() -> Vec; fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; @@ -65,10 +66,12 @@ pub mod ffi { fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); fn c_take_rust_vec_shared(v: Vec); + fn c_take_rust_vec_string(v: Vec); fn c_take_rust_vec_index(v: Vec); fn c_take_rust_vec_shared_index(v: Vec); fn c_take_rust_vec_shared_forward_iterator(v: Vec); fn c_take_ref_rust_vec(v: &Vec); + fn c_take_ref_rust_vec_string(v: &Vec); fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); /* @@ -87,6 +90,7 @@ pub mod ffi { fn c_try_return_rust_string() -> Result; fn c_try_return_unique_ptr_string() -> Result>; fn c_try_return_rust_vec() -> Result>; + fn c_try_return_rust_vec_string() -> Result>; fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec>; fn get(self: &C) -> usize; @@ -121,6 +125,7 @@ pub mod ffi { fn r_return_rust_string() -> String; fn r_return_unique_ptr_string() -> UniquePtr; fn r_return_rust_vec() -> Vec; + fn r_return_rust_vec_string() -> Vec; fn r_return_ref_rust_vec(shared: &Shared) -> &Vec; fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec; fn r_return_identity(_: usize) -> usize; @@ -138,7 +143,9 @@ pub mod ffi { fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); fn r_take_rust_vec(v: Vec); + fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); + fn r_take_ref_rust_vec_string(v: &Vec); fn r_take_enum(e: Enum); fn r_try_return_void() -> Result<()>; @@ -224,6 +231,10 @@ fn r_return_rust_vec() -> Vec { Vec::new() } +fn r_return_rust_vec_string() -> Vec { + Vec::new() +} + fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec { let _ = shared; unimplemented!() @@ -297,10 +308,18 @@ fn r_take_rust_vec(v: Vec) { let _ = v; } +fn r_take_rust_vec_string(v: Vec) { + let _ = v; +} + fn r_take_ref_rust_vec(v: &Vec) { let _ = v; } +fn r_take_ref_rust_vec_string(v: &Vec) { + let _ = v; +} + fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b213930..b45e8ae 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -119,6 +119,10 @@ rust::Vec &c_return_mut_rust_vec(C &c) { throw std::runtime_error("unimplemented"); } +rust::Vec c_return_rust_vec_string() { + throw std::runtime_error("unimplemented"); +} + size_t c_return_identity(size_t n) { return n; } size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; } @@ -241,6 +245,11 @@ void c_take_rust_vec_shared(rust::Vec v) { } } +void c_take_rust_vec_string(rust::Vec v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_rust_vec_shared_forward_iterator(rust::Vec v) { // Exercise requirements of ForwardIterator // https://en.cppreference.com/w/cpp/named_req/ForwardIterator @@ -267,6 +276,11 @@ void c_take_ref_rust_vec(const rust::Vec &v) { } } +void c_take_ref_rust_vec_string(const rust::Vec &v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_ref_rust_vec_index(const rust::Vec &v) { if (v[0] == 86 && v.at(0) == 86 && v.front() == 86 && v[1] == 75 && v.at(1) == 75 && v[3] == 9 && v.at(3) == 9 && v.back() == 9) { @@ -323,6 +337,10 @@ rust::Vec c_try_return_rust_vec() { throw std::runtime_error("unimplemented"); } +rust::Vec c_try_return_rust_vec_string() { + throw std::runtime_error("unimplemented"); +} + const rust::Vec &c_try_return_ref_rust_vec(const C &c) { (void)c; throw std::runtime_error("unimplemented"); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index fe644eb..0efbd62 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -50,6 +50,7 @@ std::vector &c_return_mut_vector(C &c); rust::Vec c_return_rust_vec(); const rust::Vec &c_return_ref_rust_vec(const C &c); rust::Vec &c_return_mut_rust_vec(C &c); +rust::Vec c_return_rust_vec_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); @@ -71,9 +72,11 @@ void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_string(rust::Vec v); void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); void c_take_ref_rust_vec(const rust::Vec &v); +void c_take_ref_rust_vec_string(const rust::Vec &v); void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); /* @@ -92,6 +95,7 @@ rust::Slice c_try_return_sliceu8(rust::Slice); rust::String c_try_return_rust_string(); std::unique_ptr c_try_return_unique_ptr_string(); rust::Vec c_try_return_rust_vec(); +rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); } // namespace tests From 47e239df1107c4cf9be1a7b522ae8820bbc10997 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 07:43:10 +0000 Subject: [PATCH 710/2232] Implement CxxVector --- diff --git a/src/cxx.cc b/src/cxx.cc index e00fd6a..2114598 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -276,7 +276,8 @@ void cxxbridge03$unique_ptr$std$string$drop( #define FOR_EACH_STD_VECTOR(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ MACRO(usize, size_t) \ - MACRO(isize, rust::isize) + MACRO(isize, rust::isize) \ + MACRO(string, std::string) #define FOR_EACH_RUST_VEC(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index bfb4a4e..10853a6 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,3 +1,4 @@ +use crate::cxx_string::CxxString; use std::ffi::c_void; use std::fmt::{self, Display}; use std::marker::PhantomData; @@ -129,16 +130,16 @@ pub unsafe trait VectorElement: Sized { unsafe fn __unique_ptr_drop(repr: *mut c_void); } -macro_rules! impl_vector_element_for_primitive { - ($ty:ident) => { +macro_rules! impl_vector_element { + ($segment:expr, $name:expr, $ty:ty) => { const_assert_eq!(1, mem::align_of::>()); unsafe impl VectorElement for $ty { - const __NAME: &'static dyn Display = &stringify!($ty); + const __NAME: &'static dyn Display = &$name; fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$size")] + #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$size")] fn __vector_size(_: &CxxVector<$ty>) -> usize; } } @@ -147,7 +148,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$get_unchecked")] + #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } @@ -156,7 +157,7 @@ macro_rules! impl_vector_element_for_primitive { fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$null")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$null")] fn __unique_ptr_null(this: *mut *mut c_void); } } @@ -167,7 +168,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$raw")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$raw")] fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); } } @@ -178,7 +179,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$get")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$get")] fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; } } @@ -187,7 +188,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$release")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$release")] fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; } } @@ -196,7 +197,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$drop")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$drop")] fn __unique_ptr_drop(this: *mut *mut c_void); } } @@ -206,6 +207,12 @@ macro_rules! impl_vector_element_for_primitive { }; } +macro_rules! impl_vector_element_for_primitive { + ($ty:ident) => { + impl_vector_element!(stringify!($ty), stringify!($ty), $ty); + }; +} + impl_vector_element_for_primitive!(u8); impl_vector_element_for_primitive!(u16); impl_vector_element_for_primitive!(u32); @@ -218,3 +225,5 @@ impl_vector_element_for_primitive!(i64); impl_vector_element_for_primitive!(isize); impl_vector_element_for_primitive!(f32); impl_vector_element_for_primitive!(f64); + +impl_vector_element!("string", "CxxString", CxxString); diff --git a/syntax/check.rs b/syntax/check.rs index 71fab3b..4fbc6e2 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -130,8 +130,8 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { match Atom::from(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) => return, - Some(CxxString) => { /* todo */ } + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) + | Some(CxxString) => return, Some(Bool) | Some(RustString) => {} } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index cb688f3..8aaac75 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -39,6 +39,7 @@ pub mod ffi { fn c_return_unique_ptr_string() -> UniquePtr; fn c_return_unique_ptr_vector_u8() -> UniquePtr>; fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_string() -> UniquePtr>; fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; @@ -62,6 +63,7 @@ pub mod ffi { fn c_take_unique_ptr_string(s: UniquePtr); fn c_take_unique_ptr_vector_u8(v: UniquePtr>); fn c_take_unique_ptr_vector_f64(v: UniquePtr>); + fn c_take_unique_ptr_vector_string(v: UniquePtr>); fn c_take_unique_ptr_vector_shared(v: UniquePtr>); fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b45e8ae..d45d5d5 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -88,6 +88,11 @@ std::unique_ptr> c_return_unique_ptr_vector_f64() { return vec; } +std::unique_ptr> c_return_unique_ptr_vector_string() { + return std::unique_ptr>( + new std::vector()); +} + std::unique_ptr> c_return_unique_ptr_vector_shared() { auto vec = std::unique_ptr>(new std::vector()); vec->push_back(Shared{1010}); @@ -210,6 +215,12 @@ void c_take_unique_ptr_vector_f64(std::unique_ptr> v) { } } +void c_take_unique_ptr_vector_string( + std::unique_ptr> v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { if (v->size() == 2) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 0efbd62..f3bc2dd 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -43,6 +43,7 @@ rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); +std::unique_ptr> c_return_unique_ptr_vector_string(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); @@ -67,6 +68,8 @@ void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); +void c_take_unique_ptr_vector_string( + std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); From ee07f011a293ba42f876825ced6a379ca89b97bc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 07:48:02 +0000 Subject: [PATCH 711/2232] Merge pull request #267 from dtolnay/vec Implement CxxVector --- diff --git a/src/cxx.cc b/src/cxx.cc index e00fd6a..2114598 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -276,7 +276,8 @@ void cxxbridge03$unique_ptr$std$string$drop( #define FOR_EACH_STD_VECTOR(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ MACRO(usize, size_t) \ - MACRO(isize, rust::isize) + MACRO(isize, rust::isize) \ + MACRO(string, std::string) #define FOR_EACH_RUST_VEC(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index bfb4a4e..10853a6 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,3 +1,4 @@ +use crate::cxx_string::CxxString; use std::ffi::c_void; use std::fmt::{self, Display}; use std::marker::PhantomData; @@ -129,16 +130,16 @@ pub unsafe trait VectorElement: Sized { unsafe fn __unique_ptr_drop(repr: *mut c_void); } -macro_rules! impl_vector_element_for_primitive { - ($ty:ident) => { +macro_rules! impl_vector_element { + ($segment:expr, $name:expr, $ty:ty) => { const_assert_eq!(1, mem::align_of::>()); unsafe impl VectorElement for $ty { - const __NAME: &'static dyn Display = &stringify!($ty); + const __NAME: &'static dyn Display = &$name; fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$size")] + #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$size")] fn __vector_size(_: &CxxVector<$ty>) -> usize; } } @@ -147,7 +148,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$get_unchecked")] + #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } @@ -156,7 +157,7 @@ macro_rules! impl_vector_element_for_primitive { fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$null")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$null")] fn __unique_ptr_null(this: *mut *mut c_void); } } @@ -167,7 +168,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$raw")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$raw")] fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); } } @@ -178,7 +179,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$get")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$get")] fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; } } @@ -187,7 +188,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$release")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$release")] fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; } } @@ -196,7 +197,7 @@ macro_rules! impl_vector_element_for_primitive { unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$drop")] + #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$drop")] fn __unique_ptr_drop(this: *mut *mut c_void); } } @@ -206,6 +207,12 @@ macro_rules! impl_vector_element_for_primitive { }; } +macro_rules! impl_vector_element_for_primitive { + ($ty:ident) => { + impl_vector_element!(stringify!($ty), stringify!($ty), $ty); + }; +} + impl_vector_element_for_primitive!(u8); impl_vector_element_for_primitive!(u16); impl_vector_element_for_primitive!(u32); @@ -218,3 +225,5 @@ impl_vector_element_for_primitive!(i64); impl_vector_element_for_primitive!(isize); impl_vector_element_for_primitive!(f32); impl_vector_element_for_primitive!(f64); + +impl_vector_element!("string", "CxxString", CxxString); diff --git a/syntax/check.rs b/syntax/check.rs index 71fab3b..4fbc6e2 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -130,8 +130,8 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { match Atom::from(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) - | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) => return, - Some(CxxString) => { /* todo */ } + | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) + | Some(CxxString) => return, Some(Bool) | Some(RustString) => {} } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index cb688f3..8aaac75 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -39,6 +39,7 @@ pub mod ffi { fn c_return_unique_ptr_string() -> UniquePtr; fn c_return_unique_ptr_vector_u8() -> UniquePtr>; fn c_return_unique_ptr_vector_f64() -> UniquePtr>; + fn c_return_unique_ptr_vector_string() -> UniquePtr>; fn c_return_unique_ptr_vector_shared() -> UniquePtr>; fn c_return_unique_ptr_vector_opaque() -> UniquePtr>; fn c_return_ref_vector(c: &C) -> &CxxVector; @@ -62,6 +63,7 @@ pub mod ffi { fn c_take_unique_ptr_string(s: UniquePtr); fn c_take_unique_ptr_vector_u8(v: UniquePtr>); fn c_take_unique_ptr_vector_f64(v: UniquePtr>); + fn c_take_unique_ptr_vector_string(v: UniquePtr>); fn c_take_unique_ptr_vector_shared(v: UniquePtr>); fn c_take_ref_vector(v: &CxxVector); fn c_take_rust_vec(v: Vec); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index b45e8ae..d45d5d5 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -88,6 +88,11 @@ std::unique_ptr> c_return_unique_ptr_vector_f64() { return vec; } +std::unique_ptr> c_return_unique_ptr_vector_string() { + return std::unique_ptr>( + new std::vector()); +} + std::unique_ptr> c_return_unique_ptr_vector_shared() { auto vec = std::unique_ptr>(new std::vector()); vec->push_back(Shared{1010}); @@ -210,6 +215,12 @@ void c_take_unique_ptr_vector_f64(std::unique_ptr> v) { } } +void c_take_unique_ptr_vector_string( + std::unique_ptr> v) { + (void)v; + cxx_test_suite_set_correct(); +} + void c_take_unique_ptr_vector_shared(std::unique_ptr> v) { if (v->size() == 2) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 0efbd62..f3bc2dd 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -43,6 +43,7 @@ rust::String c_return_rust_string(); std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); +std::unique_ptr> c_return_unique_ptr_vector_string(); std::unique_ptr> c_return_unique_ptr_vector_shared(); std::unique_ptr> c_return_unique_ptr_vector_opaque(); const std::vector &c_return_ref_vector(const C &c); @@ -67,6 +68,8 @@ void c_take_rust_string(rust::String s); void c_take_unique_ptr_string(std::unique_ptr s); void c_take_unique_ptr_vector_u8(std::unique_ptr> v); void c_take_unique_ptr_vector_f64(std::unique_ptr> v); +void c_take_unique_ptr_vector_string( + std::unique_ptr> v); void c_take_unique_ptr_vector_shared(std::unique_ptr> v); void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); From 907debe8b471d75afa0aeaab79335470d6759e8e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 28 2020 23:56:19 +0000 Subject: [PATCH 712/2232] Release 0.3.6 --- diff --git a/Cargo.toml b/Cargo.toml index 1c59ccb..a9db039 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.5" # remember to update html_root_url +version = "0.3.6" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -19,14 +19,14 @@ default = [] # c++11 "c++17" = [] [dependencies] -cxxbridge-macro = { version = "=0.3.5", path = "macro" } +cxxbridge-macro = { version = "=0.3.6", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" [dev-dependencies] -cxx-build = { version = "=0.3.5", path = "gen/build" } +cxx-build = { version = "=0.3.6", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index b242383..c59382e 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.5" +version = "0.3.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index c0cfeb4..03fb4cf 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.5" +version = "0.3.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 1b85847..2cc2a72 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.5" +version = "0.3.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 5210940..ad195cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.5")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.6")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index aea33a4..2a37462 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.5" +version = "0.3.6" dependencies = [ "cc", "cxx-build", @@ -78,7 +78,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.5" +version = "0.3.6" dependencies = [ "anyhow", "cc", @@ -98,7 +98,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.5" +version = "0.3.6" dependencies = [ "anyhow", "clap", @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "cxxbridge-macro" -version = "0.3.5" +version = "0.3.6" dependencies = [ "cxx", "proc-macro2", From de1cb777b2e4fb3f7b1255500baf7c09d2ef7a3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 00:36:33 +0000 Subject: [PATCH 713/2232] Specify consistent c++ standard between cxx and cxx-test-suite --- diff --git a/Cargo.toml b/Cargo.toml index a9db039..5f20ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,10 @@ keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] [features] -default = [] # c++11 -"c++14" = [] -"c++17" = [] +default = ["cxxbridge-flags/default"] # c++11 +"c++14" = ["cxxbridge-flags/c++14"] +"c++17" = ["cxxbridge-flags/c++17"] +"c++20" = ["cxxbridge-flags/c++20"] [dependencies] cxxbridge-macro = { version = "=0.3.6", path = "macro" } @@ -24,6 +25,7 @@ link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" +cxxbridge-flags = { version = "=0.3.6", path = "flags", default-features = false } [dev-dependencies] cxx-build = { version = "=0.3.6", path = "gen/build" } @@ -32,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] +members = ["demo-rs", "flags", "gen/build", "gen/cmd", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/build.rs b/build.rs index a412dbd..2aad99f 100644 --- a/build.rs +++ b/build.rs @@ -3,13 +3,7 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag_if_supported(if cfg!(feature = "c++17") { - "-std=c++17" - } else if cfg!(feature = "c++14") { - "-std=c++14" - } else { - "-std=c++11" - }) + .flag_if_supported(cxxbridge_flags::STD) .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); diff --git a/flags/Cargo.toml b/flags/Cargo.toml new file mode 100644 index 0000000..6e5bc5d --- /dev/null +++ b/flags/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cxxbridge-flags" +version = "0.3.6" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Compiler configuration of the `cxx` crate (implementation detail)" +repository = "https://github.com/dtolnay/cxx" + +[features] +default = [] # c++11 +"c++14" = [] +"c++17" = [] +"c++20" = [] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/flags/src/impl.rs b/flags/src/impl.rs new file mode 100644 index 0000000..4f7b8fb --- /dev/null +++ b/flags/src/impl.rs @@ -0,0 +1,20 @@ +#[allow(unused_assignments, unused_mut, unused_variables)] +pub const STD: &str = { + let mut flags = ["-std=c++11", "/std:c++11"]; + + #[cfg(feature = "c++14")] + (flags = ["-std=c++14", "/std:c++14"]); + + #[cfg(feature = "c++17")] + (flags = ["-std=c++17", "/std:c++17"]); + + #[cfg(feature = "c++20")] + (flags = ["-std=c++20", "/std:c++20"]); + + let [mut flag, msvc_flag] = flags; + + #[cfg(target_env = "msvc")] + (flag = msvc_flag); + + flag +}; diff --git a/flags/src/lib.rs b/flags/src/lib.rs new file mode 100644 index 0000000..55172b2 --- /dev/null +++ b/flags/src/lib.rs @@ -0,0 +1,7 @@ +//! This crate is an implementation detail of the `cxx` and `cxx-build` crates, +//! and does not expose any public API. + +mod r#impl; + +#[doc(hidden)] +pub use r#impl::*; diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index c2d8227..e62d090 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -12,3 +12,4 @@ cxx = { path = "../.." } [build-dependencies] cxx-build = { path = "../../gen/build" } +cxxbridge-flags = { path = "../../flags" } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index f6fa59e..8042129 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,6 +6,6 @@ fn main() { let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") - .flag_if_supported("-std=c++11") + .flag_if_supported(cxxbridge_flags::STD) .compile("cxx-test-suite"); } diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 2a37462..65ec211 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "cc", "cxx-build", "cxx-test-suite", + "cxxbridge-flags", "cxxbridge-macro", "link-cplusplus", "rustversion", @@ -94,6 +95,7 @@ version = "0.0.0" dependencies = [ "cxx", "cxx-build", + "cxxbridge-flags", ] [[package]] @@ -117,6 +119,10 @@ dependencies = [ ] [[package]] +name = "cxxbridge-flags" +version = "0.3.6" + +[[package]] name = "cxxbridge-macro" version = "0.3.6" dependencies = [ From 0f312e7a7b8721d47f2c2791a706df8395535fd5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 00:40:58 +0000 Subject: [PATCH 714/2232] Merge pull request #268 from dtolnay/flags Specify consistent c++ standard between cxx and cxx-test-suite --- diff --git a/Cargo.toml b/Cargo.toml index a9db039..5f20ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,10 @@ keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] [features] -default = [] # c++11 -"c++14" = [] -"c++17" = [] +default = ["cxxbridge-flags/default"] # c++11 +"c++14" = ["cxxbridge-flags/c++14"] +"c++17" = ["cxxbridge-flags/c++17"] +"c++20" = ["cxxbridge-flags/c++20"] [dependencies] cxxbridge-macro = { version = "=0.3.6", path = "macro" } @@ -24,6 +25,7 @@ link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" +cxxbridge-flags = { version = "=0.3.6", path = "flags", default-features = false } [dev-dependencies] cxx-build = { version = "=0.3.6", path = "gen/build" } @@ -32,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] +members = ["demo-rs", "flags", "gen/build", "gen/cmd", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/build.rs b/build.rs index a412dbd..2aad99f 100644 --- a/build.rs +++ b/build.rs @@ -3,13 +3,7 @@ fn main() { .file("src/cxx.cc") .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate - .flag_if_supported(if cfg!(feature = "c++17") { - "-std=c++17" - } else if cfg!(feature = "c++14") { - "-std=c++14" - } else { - "-std=c++11" - }) + .flag_if_supported(cxxbridge_flags::STD) .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); diff --git a/flags/Cargo.toml b/flags/Cargo.toml new file mode 100644 index 0000000..6e5bc5d --- /dev/null +++ b/flags/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cxxbridge-flags" +version = "0.3.6" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Compiler configuration of the `cxx` crate (implementation detail)" +repository = "https://github.com/dtolnay/cxx" + +[features] +default = [] # c++11 +"c++14" = [] +"c++17" = [] +"c++20" = [] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/flags/src/impl.rs b/flags/src/impl.rs new file mode 100644 index 0000000..4f7b8fb --- /dev/null +++ b/flags/src/impl.rs @@ -0,0 +1,20 @@ +#[allow(unused_assignments, unused_mut, unused_variables)] +pub const STD: &str = { + let mut flags = ["-std=c++11", "/std:c++11"]; + + #[cfg(feature = "c++14")] + (flags = ["-std=c++14", "/std:c++14"]); + + #[cfg(feature = "c++17")] + (flags = ["-std=c++17", "/std:c++17"]); + + #[cfg(feature = "c++20")] + (flags = ["-std=c++20", "/std:c++20"]); + + let [mut flag, msvc_flag] = flags; + + #[cfg(target_env = "msvc")] + (flag = msvc_flag); + + flag +}; diff --git a/flags/src/lib.rs b/flags/src/lib.rs new file mode 100644 index 0000000..55172b2 --- /dev/null +++ b/flags/src/lib.rs @@ -0,0 +1,7 @@ +//! This crate is an implementation detail of the `cxx` and `cxx-build` crates, +//! and does not expose any public API. + +mod r#impl; + +#[doc(hidden)] +pub use r#impl::*; diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml index c2d8227..e62d090 100644 --- a/tests/ffi/Cargo.toml +++ b/tests/ffi/Cargo.toml @@ -12,3 +12,4 @@ cxx = { path = "../.." } [build-dependencies] cxx-build = { path = "../../gen/build" } +cxxbridge-flags = { path = "../../flags" } diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index f6fa59e..8042129 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,6 +6,6 @@ fn main() { let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") - .flag_if_supported("-std=c++11") + .flag_if_supported(cxxbridge_flags::STD) .compile("cxx-test-suite"); } diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 2a37462..65ec211 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "cc", "cxx-build", "cxx-test-suite", + "cxxbridge-flags", "cxxbridge-macro", "link-cplusplus", "rustversion", @@ -94,6 +95,7 @@ version = "0.0.0" dependencies = [ "cxx", "cxx-build", + "cxxbridge-flags", ] [[package]] @@ -117,6 +119,10 @@ dependencies = [ ] [[package]] +name = "cxxbridge-flags" +version = "0.3.6" + +[[package]] name = "cxxbridge-macro" version = "0.3.6" dependencies = [ From dab7e80a818491b5652052f96cd46c013c7ac28d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 01:54:53 +0000 Subject: [PATCH 715/2232] Preserve utf-8 invariant on generated code We only write to the output buffer using the `write!` macros, which go through std::fmt and are guaranteed that the data written is utf-8. --- diff --git a/gen/src/out.rs b/gen/src/out.rs index c21ad78..dbb1d73 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -12,7 +12,7 @@ pub(crate) struct OutFile { } pub struct Content { - bytes: Vec, + bytes: String, section_pending: bool, blocks_pending: Vec<&'static str>, } @@ -42,9 +42,9 @@ impl OutFile { pub fn end_block(&mut self, block: &'static str) { let content = self.content.get_mut(); if content.blocks_pending.pop().is_none() { - content.bytes.extend_from_slice(b"} // "); - content.bytes.extend_from_slice(block.as_bytes()); - content.bytes.push(b'\n'); + content.bytes.push_str("} // "); + content.bytes.push_str(block); + content.bytes.push('\n'); content.section_pending = true; } } @@ -58,19 +58,19 @@ impl OutFile { let front = &self.front.bytes; let content = &self.content.borrow().bytes; let len = front.len() + !front.is_empty() as usize + content.len(); - let mut out = Vec::with_capacity(len); - out.extend_from_slice(front); + let mut out = String::with_capacity(len); + out.push_str(front); if !front.is_empty() { - out.push(b'\n'); + out.push('\n'); } - out.extend_from_slice(content); - out + out.push_str(content); + out.into_bytes() } } impl Write for Content { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_bytes(s.as_bytes()); + self.write(s); Ok(()) } } @@ -82,30 +82,30 @@ impl Content { fn new() -> Self { Content { - bytes: Vec::new(), + bytes: String::new(), section_pending: false, blocks_pending: Vec::new(), } } - fn write_bytes(&mut self, b: &[u8]) { + fn write(&mut self, b: &str) { if !b.is_empty() { if !self.blocks_pending.is_empty() { if !self.bytes.is_empty() { - self.bytes.push(b'\n'); + self.bytes.push('\n'); } for block in self.blocks_pending.drain(..) { - self.bytes.extend_from_slice(block.as_bytes()); - self.bytes.extend_from_slice(b" {\n"); + self.bytes.push_str(block); + self.bytes.push_str(" {\n"); } self.section_pending = false; } else if self.section_pending { if !self.bytes.is_empty() { - self.bytes.push(b'\n'); + self.bytes.push('\n'); } self.section_pending = false; } - self.bytes.extend_from_slice(b); + self.bytes.push_str(b); } } } From 020c923e6ba251837e25b70d0e64982a11cfd526 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 18:34:00 +0000 Subject: [PATCH 716/2232] Less brittle matching of cxx::bridge path --- diff --git a/gen/src/find.rs b/gen/src/find.rs index 86e1dc7..d897deb 100644 --- a/gen/src/find.rs +++ b/gen/src/find.rs @@ -1,6 +1,5 @@ use crate::gen::{Error, Input, Result}; use crate::syntax::namespace::Namespace; -use quote::quote; use syn::{Attribute, File, Item}; pub(super) fn find_bridge_mod(syntax: File) -> Result { @@ -14,8 +13,8 @@ fn scan(items: Vec) -> Result> { for item in items { if let Item::Mod(item) = item { for attr in &item.attrs { - let path = &attr.path; - if quote!(#path).to_string() == "cxx :: bridge" { + let path = &attr.path.segments; + if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { let module = match item.content { Some(module) => module.1, None => { From 5e668bce9b712aa2246a1a8918c2ee4145c2c410 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 18:36:22 +0000 Subject: [PATCH 717/2232] Format ui test files using rustfmt --- diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs index a9ad533..987004b 100644 --- a/tests/ui/empty_enum.rs +++ b/tests/ui/empty_enum.rs @@ -1,8 +1,6 @@ #[cxx::bridge] mod ffi { - enum A { - - } + enum A {} } fn main() {} diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr index c73578a..7f35019 100644 --- a/tests/ui/empty_enum.stderr +++ b/tests/ui/empty_enum.stderr @@ -1,7 +1,5 @@ error: enums without any variants are not supported --> $DIR/empty_enum.rs:3:5 | -3 | / enum A { -4 | | -5 | | } - | |_____^ +3 | enum A {} + | ^^^^^^^^^ diff --git a/tests/ui/multiple_parse_error.rs b/tests/ui/multiple_parse_error.rs index 061eab6..138d6d6 100644 --- a/tests/ui/multiple_parse_error.rs +++ b/tests/ui/multiple_parse_error.rs @@ -2,8 +2,7 @@ mod ffi { struct Monad; - extern "Haskell" { - } + extern "Haskell" {} } fn main() {} diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr index 854aa90..4189507 100644 --- a/tests/ui/multiple_parse_error.stderr +++ b/tests/ui/multiple_parse_error.stderr @@ -7,5 +7,5 @@ error: struct with generic parameters is not supported yet error: unrecognized ABI --> $DIR/multiple_parse_error.rs:5:5 | -5 | extern "Haskell" { +5 | extern "Haskell" {} | ^^^^^^^^^^^^^^^^ From 05ef6ffa28719132df8967edc80ba45907166318 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 18:55:04 +0000 Subject: [PATCH 718/2232] Data structure to represent possibly unsafe module --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 0869c2c..48ef6a3 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,21 +1,20 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, Error, ItemMod, Result, Token}; +use std::mem; +use syn::{parse_quote, Result, Token}; -pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { +pub fn bridge(namespace: &Namespace, mut ffi: Module) -> Result { let ref mut errors = Errors::new(); - let content = ffi.content.take().ok_or(Error::new( - Span::call_site(), - "#[cxx::bridge] module must have inline contents", - ))?; - let ref apis = syntax::parse_items(errors, content.1); + let content = mem::take(&mut ffi.content); + let ref apis = syntax::parse_items(errors, content); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); @@ -24,7 +23,7 @@ pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { Ok(expand(namespace, ffi, apis, types)) } -fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream { +fn expand(namespace: &Namespace, ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index bc8d19b..b8a592b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,9 +13,10 @@ mod expand; mod syntax; mod type_id; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod, LitStr}; +use syn::{parse_macro_input, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -39,7 +40,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = parse_macro_input!(args as Namespace); - let ffi = parse_macro_input!(input as ItemMod); + let ffi = parse_macro_input!(input as Module); expand::bridge(&namespace, ffi) .unwrap_or_else(|err| err.to_compile_error()) diff --git a/syntax/file.rs b/syntax/file.rs new file mode 100644 index 0000000..132a986 --- /dev/null +++ b/syntax/file.rs @@ -0,0 +1,47 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; + +pub struct Module { + pub attrs: Vec, + pub vis: Visibility, + // TODO: unsafety + pub mod_token: Token![mod], + pub ident: Ident, + pub brace_token: token::Brace, + pub content: Vec, +} + +impl Parse for Module { + fn parse(input: ParseStream) -> Result { + let mut attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let mod_token: Token![mod] = input.parse()?; + let ident: Ident = input.parse()?; + + if input.peek(Token![;]) { + return Err(Error::new( + Span::call_site(), + "#[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 { + attrs, + vis, + mod_token, + ident, + brace_token, + content: items, + }) + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index cd6f235..ae37802 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod derive; mod discriminant; mod doc; pub mod error; +pub mod file; pub mod ident; mod impls; pub mod mangle; From fcd8f463a0fbba06ee1e3b06d017e5a8b0fcc6a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 19:13:52 +0000 Subject: [PATCH 719/2232] Data structure for source file --- diff --git a/gen/src/file.rs b/gen/src/file.rs new file mode 100644 index 0000000..3b45fe7 --- /dev/null +++ b/gen/src/file.rs @@ -0,0 +1,20 @@ +use syn::parse::{Parse, ParseStream, Result}; +use syn::{Attribute, Item}; + +pub struct File { + pub attrs: Vec, + pub items: Vec, +} + +impl Parse for File { + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_inner)?; + + let mut items = Vec::new(); + while !input.is_empty() { + items.push(input.parse()?); + } + + Ok(File { attrs, items }) + } +} diff --git a/gen/src/find.rs b/gen/src/find.rs index d897deb..d4608c8 100644 --- a/gen/src/find.rs +++ b/gen/src/find.rs @@ -1,6 +1,6 @@ -use crate::gen::{Error, Input, Result}; +use crate::gen::{Error, File, Input, Result}; use crate::syntax::namespace::Namespace; -use syn::{Attribute, File, Item}; +use syn::{Attribute, Item}; pub(super) fn find_bridge_mod(syntax: File) -> Result { match scan(syntax.items)? { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 710bca2..8df3404 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,6 +2,7 @@ // the cxxbridge CLI command. mod error; +mod file; mod find; pub(super) mod include; pub(super) mod out; @@ -11,6 +12,7 @@ mod write; mod tests; use self::error::{format_err, Error, Result}; +use self::file::File; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; @@ -56,7 +58,7 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { fn generate(source: &str, opt: Opt, header: bool) -> Result> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; + let syntax: File = syn::parse_str(source)?; let bridge = find::find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; let ref apis = syntax::parse_items(errors, bridge.module); From 17c3230d08481f23e7031ee8bd2ff59bba117837 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 19:21:16 +0000 Subject: [PATCH 720/2232] Skip past shebang in source file --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 8df3404..0aa149f 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -49,9 +49,14 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match generate(&source, opt, header) { + let mut source = source.as_str(); + if source.starts_with("#!") && !source.starts_with("#![") { + let shebang_end = source.find('\n').unwrap_or(source.len()); + source = &source[shebang_end..]; + } + match generate(source, opt, header) { Ok(out) => out, - Err(err) => format_err(path, &source, err), + Err(err) => format_err(path, source, err), } } From 3c64a4e144a4b7f476345e038212747fd6feb5c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 21:36:42 +0000 Subject: [PATCH 721/2232] Parse full file using the new Module parser --- diff --git a/gen/src/error.rs b/gen/src/error.rs index 51dbbe7..537351f 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -16,7 +16,6 @@ pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { NoBridgeMod, - OutOfLineMod, Io(io::Error), Syn(syn::Error), } @@ -25,7 +24,6 @@ impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), Error::Io(err) => err.fmt(f), Error::Syn(err) => err.fmt(f), } diff --git a/gen/src/file.rs b/gen/src/file.rs index 3b45fe7..a2f86e6 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -1,20 +1,71 @@ -use syn::parse::{Parse, ParseStream, Result}; -use syn::{Attribute, Item}; +use crate::syntax::file::Module; +use crate::syntax::namespace::Namespace; +use syn::parse::discouraged::Speculative; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{braced, Attribute, Ident, Item, Token, Visibility}; pub struct File { - pub attrs: Vec, - pub items: Vec, + pub modules: Vec, } impl Parse for File { fn parse(input: ParseStream) -> Result { - let attrs = input.call(Attribute::parse_inner)?; + let mut modules = Vec::new(); + input.call(Attribute::parse_inner)?; + parse(input, &mut modules)?; + Ok(File { modules }) + } +} + +fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { + while !input.is_empty() { + let mut cxx_bridge = false; + let mut namespace = Namespace::none(); + let attrs = input.call(Attribute::parse_outer)?; + for attr in &attrs { + let path = &attr.path.segments; + if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { + cxx_bridge = true; + namespace = parse_args(attr)?; + break; + } + } - let mut items = Vec::new(); - while !input.is_empty() { - items.push(input.parse()?); + let ahead = input.fork(); + ahead.parse::()?; + ahead.parse::>()?; + if !ahead.peek(Token![mod]) { + let item: Item = input.parse()?; + if cxx_bridge { + return Err(Error::new_spanned(item, "expected a module")); + } + continue; } - Ok(File { attrs, items }) + if cxx_bridge { + let mut module: Module = input.parse()?; + module.namespace = namespace; + module.attrs = attrs; + modules.push(module); + } else { + input.advance_to(&ahead); + input.parse::()?; + input.parse::()?; + let semi: Option = input.parse()?; + if semi.is_none() { + let content; + braced!(content in input); + parse(&content, modules)?; + } + } + } + Ok(()) +} + +fn parse_args(attr: &Attribute) -> Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() } } diff --git a/gen/src/find.rs b/gen/src/find.rs deleted file mode 100644 index d4608c8..0000000 --- a/gen/src/find.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::gen::{Error, File, Input, Result}; -use crate::syntax::namespace::Namespace; -use syn::{Attribute, Item}; - -pub(super) fn find_bridge_mod(syntax: File) -> Result { - match scan(syntax.items)? { - Some(input) => Ok(input), - None => Err(Error::NoBridgeMod), - } -} - -fn scan(items: Vec) -> Result> { - for item in items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path.segments; - if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Some(Input { namespace, module })); - } - } - if let Some(module) = item.content { - if let Some(input) = scan(module.1)? { - return Ok(Some(input)); - } - } - } - } - Ok(None) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 0aa149f..fa9e407 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -3,7 +3,6 @@ mod error; mod file; -mod find; pub(super) mod include; pub(super) mod out; mod write; @@ -13,17 +12,10 @@ mod tests; use self::error::{format_err, Error, Result}; use self::file::File; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use std::fs; use std::path::Path; -use syn::Item; - -struct Input { - namespace: Namespace, - module: Vec, -} #[derive(Default)] pub(super) struct Opt { @@ -64,9 +56,13 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let syntax: File = syn::parse_str(source)?; - let bridge = find::find_bridge_mod(syntax)?; + let bridge = syntax + .modules + .into_iter() + .next() + .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.module); + let ref apis = syntax::parse_items(errors, bridge.content); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 48ef6a3..aecec75 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -11,21 +11,23 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::{parse_quote, Result, Token}; -pub fn bridge(namespace: &Namespace, mut ffi: Module) -> Result { +pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let ref apis = syntax::parse_items(errors, content); let ref types = Types::collect(errors, apis); errors.propagate()?; + let namespace = &ffi.namespace; check::typecheck(errors, namespace, apis, types); errors.propagate()?; - Ok(expand(namespace, ffi, apis, types)) + Ok(expand(ffi, apis, types)) } -fn expand(namespace: &Namespace, ffi: Module, apis: &[Api], types: &Types) -> TokenStream { +fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); + let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b8a592b..c2ad38b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -40,9 +40,10 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = parse_macro_input!(args as Namespace); - let ffi = parse_macro_input!(input as Module); + let mut ffi = parse_macro_input!(input as Module); + ffi.namespace = namespace; - expand::bridge(&namespace, ffi) + expand::bridge(ffi) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/syntax/file.rs b/syntax/file.rs index 132a986..dc7c295 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -1,8 +1,10 @@ -use proc_macro2::Span; +use crate::syntax::namespace::Namespace; +use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; pub struct Module { + pub namespace: Namespace, pub attrs: Vec, pub vis: Visibility, // TODO: unsafety @@ -14,14 +16,17 @@ pub struct Module { impl Parse for Module { fn parse(input: ParseStream) -> Result { + let namespace = Namespace::none(); let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let mod_token: Token![mod] = input.parse()?; let ident: Ident = input.parse()?; - if input.peek(Token![;]) { - return Err(Error::new( - Span::call_site(), + let semi: Option = 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", ))?; } @@ -36,6 +41,7 @@ impl Parse for Module { } Ok(Module { + namespace, attrs, vis, mod_token, From 633f5669fa03e72917720eb44ec89811ea998907 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 21:47:45 +0000 Subject: [PATCH 722/2232] Parse unsafety on module --- diff --git a/syntax/file.rs b/syntax/file.rs index dc7c295..2243235 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -7,7 +7,7 @@ pub struct Module { pub namespace: Namespace, pub attrs: Vec, pub vis: Visibility, - // TODO: unsafety + pub unsafety: Option, pub mod_token: Token![mod], pub ident: Ident, pub brace_token: token::Brace, @@ -19,6 +19,7 @@ impl Parse for Module { let namespace = Namespace::none(); let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; + let unsafety: Option = input.parse()?; let mod_token: Token![mod] = input.parse()?; let ident: Ident = input.parse()?; @@ -44,6 +45,7 @@ impl Parse for Module { namespace, attrs, vis, + unsafety, mod_token, ident, brace_token, From 00a83852cd9362c2e8bb6c4889801e3980d402c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 22:11:37 +0000 Subject: [PATCH 723/2232] Data structure for parsed contents of bridge module --- diff --git a/syntax/file.rs b/syntax/file.rs index 2243235..f227900 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -1,7 +1,10 @@ use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; -use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; +use syn::{ + braced, token, Attribute, Ident, Item as RustItem, ItemEnum, ItemForeignMod, ItemStruct, + ItemUse, Token, Visibility, +}; pub struct Module { pub namespace: Namespace, @@ -14,6 +17,14 @@ pub struct Module { pub content: Vec, } +pub enum Item { + Struct(ItemStruct), + Enum(ItemEnum), + ForeignMod(ItemForeignMod), + Use(ItemUse), + Other(RustItem), +} + impl Parse for Module { fn parse(input: ParseStream) -> Result { let namespace = Namespace::none(); @@ -53,3 +64,16 @@ impl Parse for Module { }) } } + +impl Parse for Item { + fn parse(input: ParseStream) -> Result { + let item = input.parse()?; + match item { + RustItem::Struct(item) => Ok(Item::Struct(item)), + RustItem::Enum(item) => Ok(Item::Enum(item)), + RustItem::ForeignMod(item) => Ok(Item::ForeignMod(item)), + RustItem::Use(item) => Ok(Item::Use(item)), + other => Ok(Item::Other(other)), + } + } +} diff --git a/syntax/parse.rs b/syntax/parse.rs index e195081..117d025 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,4 +1,5 @@ use crate::syntax::discriminant::DiscriminantSet; +use crate::syntax::file::Item; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -11,7 +12,7 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, + GenericArgument, Ident, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; @@ -33,7 +34,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), - _ => cx.error(item, "unsupported item"), + Item::Other(item) => cx.error(item, "unsupported item"), } } apis From c598a279025c8a89b029b2899fccfe6f06f3663a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 22:12:46 +0000 Subject: [PATCH 724/2232] Represent the unsafety on foreign module --- diff --git a/syntax/file.rs b/syntax/file.rs index f227900..dfe8e0d 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -2,7 +2,7 @@ use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ - braced, token, Attribute, Ident, Item as RustItem, ItemEnum, ItemForeignMod, ItemStruct, + braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, ItemUse, Token, Visibility, }; @@ -25,6 +25,14 @@ pub enum Item { Other(RustItem), } +pub struct ItemForeignMod { + pub attrs: Vec, + pub unsafety: Option, + pub abi: Abi, + pub brace_token: token::Brace, + pub items: Vec, +} + impl Parse for Module { fn parse(input: ParseStream) -> Result { let namespace = Namespace::none(); @@ -71,7 +79,13 @@ impl Parse for Item { match item { RustItem::Struct(item) => Ok(Item::Struct(item)), RustItem::Enum(item) => Ok(Item::Enum(item)), - RustItem::ForeignMod(item) => Ok(Item::ForeignMod(item)), + RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { + attrs: item.attrs, + unsafety: None, + abi: item.abi, + brace_token: item.brace_token, + items: item.items, + })), RustItem::Use(item) => Ok(Item::Use(item)), other => Ok(Item::Other(other)), } diff --git a/syntax/parse.rs b/syntax/parse.rs index 117d025..c23ee8f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,5 @@ use crate::syntax::discriminant::DiscriminantSet; -use crate::syntax::file::Item; +use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -12,8 +12,8 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, + Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { From 02550c04a0cb3b3def55f2c16fa9f55ccf7bc0f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 22:20:50 +0000 Subject: [PATCH 725/2232] Preserve inner attrs inside bridge module --- diff --git a/gen/src/file.rs b/gen/src/file.rs index a2f86e6..c696407 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -21,7 +21,7 @@ fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { while !input.is_empty() { let mut cxx_bridge = false; let mut namespace = Namespace::none(); - let attrs = input.call(Attribute::parse_outer)?; + let mut attrs = input.call(Attribute::parse_outer)?; for attr in &attrs { let path = &attr.path.segments; if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { @@ -45,6 +45,7 @@ fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { if cxx_bridge { let mut module: Module = input.parse()?; module.namespace = namespace; + attrs.extend(module.attrs); module.attrs = attrs; modules.push(module); } else { From 0c0cfee26499acee4563eb281809cbc543c2584c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 22:30:53 +0000 Subject: [PATCH 726/2232] Parse unsafety on extern blocks --- diff --git a/syntax/file.rs b/syntax/file.rs index dfe8e0d..2141c17 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -3,7 +3,7 @@ use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, - ItemUse, Token, Visibility, + ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -75,18 +75,31 @@ impl Parse for Module { impl Parse for Item { fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let ahead = input.fork(); + let unsafety = if ahead.parse::>()?.is_some() + && ahead.parse::>()?.is_some() + && ahead.parse::>().is_ok() + && ahead.peek(token::Brace) + { + Some(input.parse()?) + } else { + None + }; + let item = input.parse()?; match item { - RustItem::Struct(item) => Ok(Item::Struct(item)), - RustItem::Enum(item) => Ok(Item::Enum(item)), + RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })), + RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })), RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { attrs: item.attrs, - unsafety: None, + unsafety, abi: item.abi, brace_token: item.brace_token, items: item.items, })), - RustItem::Use(item) => Ok(Item::Use(item)), + RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), other => Ok(Item::Other(other)), } } From 3c01ac2383524f772f1f03a8b414136a53c147b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 22:50:33 +0000 Subject: [PATCH 727/2232] Merge pull request #270 from dtolnay/parse Parse unsafe modules and unsafe extern blocks --- diff --git a/gen/src/error.rs b/gen/src/error.rs index 51dbbe7..537351f 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -16,7 +16,6 @@ pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { NoBridgeMod, - OutOfLineMod, Io(io::Error), Syn(syn::Error), } @@ -25,7 +24,6 @@ impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), Error::Io(err) => err.fmt(f), Error::Syn(err) => err.fmt(f), } diff --git a/gen/src/file.rs b/gen/src/file.rs new file mode 100644 index 0000000..c696407 --- /dev/null +++ b/gen/src/file.rs @@ -0,0 +1,72 @@ +use crate::syntax::file::Module; +use crate::syntax::namespace::Namespace; +use syn::parse::discouraged::Speculative; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{braced, Attribute, Ident, Item, Token, Visibility}; + +pub struct File { + pub modules: Vec, +} + +impl Parse for File { + fn parse(input: ParseStream) -> Result { + let mut modules = Vec::new(); + input.call(Attribute::parse_inner)?; + parse(input, &mut modules)?; + Ok(File { modules }) + } +} + +fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { + while !input.is_empty() { + let mut cxx_bridge = false; + let mut namespace = Namespace::none(); + let mut attrs = input.call(Attribute::parse_outer)?; + for attr in &attrs { + let path = &attr.path.segments; + if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { + cxx_bridge = true; + namespace = parse_args(attr)?; + break; + } + } + + let ahead = input.fork(); + ahead.parse::()?; + ahead.parse::>()?; + if !ahead.peek(Token![mod]) { + let item: Item = input.parse()?; + if cxx_bridge { + return Err(Error::new_spanned(item, "expected a module")); + } + continue; + } + + if cxx_bridge { + let mut module: Module = input.parse()?; + module.namespace = namespace; + attrs.extend(module.attrs); + module.attrs = attrs; + modules.push(module); + } else { + input.advance_to(&ahead); + input.parse::()?; + input.parse::()?; + let semi: Option = input.parse()?; + if semi.is_none() { + let content; + braced!(content in input); + parse(&content, modules)?; + } + } + } + Ok(()) +} + +fn parse_args(attr: &Attribute) -> Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() + } +} diff --git a/gen/src/find.rs b/gen/src/find.rs deleted file mode 100644 index d897deb..0000000 --- a/gen/src/find.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::gen::{Error, Input, Result}; -use crate::syntax::namespace::Namespace; -use syn::{Attribute, File, Item}; - -pub(super) fn find_bridge_mod(syntax: File) -> Result { - match scan(syntax.items)? { - Some(input) => Ok(input), - None => Err(Error::NoBridgeMod), - } -} - -fn scan(items: Vec) -> Result> { - for item in items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path.segments; - if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Some(Input { namespace, module })); - } - } - if let Some(module) = item.content { - if let Some(input) = scan(module.1)? { - return Ok(Some(input)); - } - } - } - } - Ok(None) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 710bca2..fa9e407 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,7 +2,7 @@ // the cxxbridge CLI command. mod error; -mod find; +mod file; pub(super) mod include; pub(super) mod out; mod write; @@ -11,17 +11,11 @@ mod write; mod tests; use self::error::{format_err, Error, Result}; -use crate::syntax::namespace::Namespace; +use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use std::fs; use std::path::Path; -use syn::Item; - -struct Input { - namespace: Namespace, - module: Vec, -} #[derive(Default)] pub(super) struct Opt { @@ -47,19 +41,28 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match generate(&source, opt, header) { + let mut source = source.as_str(); + if source.starts_with("#!") && !source.starts_with("#![") { + let shebang_end = source.find('\n').unwrap_or(source.len()); + source = &source[shebang_end..]; + } + match generate(source, opt, header) { Ok(out) => out, - Err(err) => format_err(path, &source, err), + Err(err) => format_err(path, source, err), } } fn generate(source: &str, opt: Opt, header: bool) -> Result> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; - let bridge = find::find_bridge_mod(syntax)?; + let syntax: File = syn::parse_str(source)?; + let bridge = syntax + .modules + .into_iter() + .next() + .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.module); + let ref apis = syntax::parse_items(errors, bridge.content); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 0869c2c..aecec75 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,32 +1,33 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, Error, ItemMod, Result, Token}; +use std::mem; +use syn::{parse_quote, Result, Token}; -pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { +pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); - let content = ffi.content.take().ok_or(Error::new( - Span::call_site(), - "#[cxx::bridge] module must have inline contents", - ))?; - let ref apis = syntax::parse_items(errors, content.1); + let content = mem::take(&mut ffi.content); + let ref apis = syntax::parse_items(errors, content); let ref types = Types::collect(errors, apis); errors.propagate()?; + let namespace = &ffi.namespace; check::typecheck(errors, namespace, apis, types); errors.propagate()?; - Ok(expand(namespace, ffi, apis, types)) + Ok(expand(ffi, apis, types)) } -fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream { +fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); + let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { diff --git a/macro/src/lib.rs b/macro/src/lib.rs index bc8d19b..c2ad38b 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,9 +13,10 @@ mod expand; mod syntax; mod type_id; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod, LitStr}; +use syn::{parse_macro_input, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -39,9 +40,10 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = parse_macro_input!(args as Namespace); - let ffi = parse_macro_input!(input as ItemMod); + let mut ffi = parse_macro_input!(input as Module); + ffi.namespace = namespace; - expand::bridge(&namespace, ffi) + expand::bridge(ffi) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/syntax/file.rs b/syntax/file.rs new file mode 100644 index 0000000..2141c17 --- /dev/null +++ b/syntax/file.rs @@ -0,0 +1,106 @@ +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, ItemStruct, + ItemUse, LitStr, Token, Visibility, +}; + +pub struct Module { + pub namespace: Namespace, + pub attrs: Vec, + pub vis: Visibility, + pub unsafety: Option, + pub mod_token: Token![mod], + pub ident: Ident, + pub brace_token: token::Brace, + pub content: Vec, +} + +pub enum Item { + Struct(ItemStruct), + Enum(ItemEnum), + ForeignMod(ItemForeignMod), + Use(ItemUse), + Other(RustItem), +} + +pub struct ItemForeignMod { + pub attrs: Vec, + pub unsafety: Option, + pub abi: Abi, + pub brace_token: token::Brace, + pub items: Vec, +} + +impl Parse for Module { + fn parse(input: ParseStream) -> Result { + let namespace = Namespace::none(); + let mut attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let unsafety: Option = input.parse()?; + let mod_token: Token![mod] = input.parse()?; + let ident: Ident = input.parse()?; + + let semi: Option = 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 { + namespace, + attrs, + vis, + unsafety, + mod_token, + ident, + brace_token, + content: items, + }) + } +} + +impl Parse for Item { + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let ahead = input.fork(); + let unsafety = if ahead.parse::>()?.is_some() + && ahead.parse::>()?.is_some() + && ahead.parse::>().is_ok() + && ahead.peek(token::Brace) + { + Some(input.parse()?) + } else { + None + }; + + let item = input.parse()?; + match item { + RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })), + RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })), + RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { + attrs: item.attrs, + unsafety, + abi: item.abi, + brace_token: item.brace_token, + items: item.items, + })), + RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), + other => Ok(Item::Other(other)), + } + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index cd6f235..ae37802 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod derive; mod discriminant; mod doc; pub mod error; +pub mod file; pub mod ident; mod impls; pub mod mangle; diff --git a/syntax/parse.rs b/syntax/parse.rs index e195081..c23ee8f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,4 +1,5 @@ use crate::syntax::discriminant::DiscriminantSet; +use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -11,8 +12,8 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, + Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -33,7 +34,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), - _ => cx.error(item, "unsupported item"), + Item::Other(item) => cx.error(item, "unsupported item"), } } apis From 0b3eef7435233d50b99dbeac1a665116821ec404 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 29 2020 23:15:09 +0000 Subject: [PATCH 728/2232] Clean up some unneeded clones of the type names --- diff --git a/syntax/types.rs b/syntax/types.rs index ba9bc78..2f87d96 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -8,11 +8,11 @@ use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; pub struct Types<'a> { pub all: Set<'a, Type>, - pub structs: Map, - pub enums: Map, + pub structs: Map<&'a Ident, &'a Struct>, + pub enums: Map<&'a Ident, &'a Enum>, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, - pub aliases: Map, + pub aliases: Map<&'a Ident, &'a TypeAlias>, } impl<'a> Types<'a> { @@ -53,7 +53,7 @@ impl<'a> Types<'a> { Api::Struct(strct) => { let ident = &strct.ident; if type_names.insert(ident) { - structs.insert(ident.clone(), strct); + structs.insert(ident, strct); } else { duplicate_name(cx, strct, ident); } @@ -68,7 +68,7 @@ impl<'a> Types<'a> { if !type_names.insert(ident) && !cxx.contains(ident) { duplicate_name(cx, enm, ident); } - enums.insert(ident.clone(), enm); + enums.insert(ident, enm); } Api::CxxType(ety) => { let ident = &ety.ident; @@ -104,7 +104,7 @@ impl<'a> Types<'a> { duplicate_name(cx, alias, ident); } cxx.insert(ident); - aliases.insert(ident.clone(), alias); + aliases.insert(ident, alias); } } } From 554837220f8b38131b51aa7d56374c3235209ca0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 01:27:46 +0000 Subject: [PATCH 729/2232] Add enum classifier for error messages --- diff --git a/syntax/check.rs b/syntax/check.rs index 4fbc6e2..22ae58e 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -355,6 +355,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String { Type::Ident(ident) => { if cx.types.structs.contains_key(ident) { "struct".to_owned() + } else if cx.types.enums.contains_key(ident) { + "enum".to_owned() } else if cx.types.cxx.contains(ident) { "C++ type".to_owned() } else if cx.types.rust.contains(ident) { From 8984995600655d86d8a0c524689132256ecd3d3f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 01:35:31 +0000 Subject: [PATCH 730/2232] Box, Vec of an extern enum is supposed to work --- diff --git a/syntax/check.rs b/syntax/check.rs index 22ae58e..7b84b18 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -71,7 +71,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.cxx.contains(ident) { + if cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) { cx.error(ptr, error::BOX_CXX_TYPE.msg); } @@ -85,7 +85,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { if let Type::Ident(ident) = &ty.inner { - if cx.types.cxx.contains(ident) { + if cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) { cx.error(ty, "Rust Vec containing C++ type is not supported yet"); return; } @@ -320,7 +320,9 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; - ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident) + ident == CxxString + || cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) + || cx.types.rust.contains(ident) } fn span_for_struct_error(strct: &Struct) -> TokenStream { From c880ae2c34705d84cede18ccf634b9a170cb0938 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 01:46:50 +0000 Subject: [PATCH 731/2232] Parse extern C++ structs --- diff --git a/syntax/check.rs b/syntax/check.rs index 7b84b18..5980f75 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -71,7 +71,10 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) { + if cx.types.cxx.contains(ident) + && !cx.types.structs.contains_key(ident) + && !cx.types.enums.contains_key(ident) + { cx.error(ptr, error::BOX_CXX_TYPE.msg); } @@ -85,7 +88,10 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { if let Type::Ident(ident) = &ty.inner { - if cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) { + if cx.types.cxx.contains(ident) + && !cx.types.structs.contains_key(ident) + && !cx.types.enums.contains_key(ident) + { cx.error(ty, "Rust Vec containing C++ type is not supported yet"); return; } @@ -168,6 +174,11 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { cx.error(span, "structs without any fields are not supported"); } + if cx.types.cxx.contains(&strct.ident) { + let span = span_for_struct_error(strct); + cx.error(span, "extern C++ structs are not implemented yet"); + } + for field in &strct.fields { if is_unsized(cx, &field.ty) { let desc = describe(cx, &field.ty); @@ -321,7 +332,9 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { _ => return false, }; ident == CxxString - || cx.types.cxx.contains(ident) && !cx.types.enums.contains_key(ident) + || cx.types.cxx.contains(ident) + && !cx.types.structs.contains_key(ident) + && !cx.types.enums.contains_key(ident) || cx.types.rust.contains(ident) } diff --git a/syntax/types.rs b/syntax/types.rs index 2f87d96..d579c78 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -48,33 +48,54 @@ impl<'a> Types<'a> { 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.ident; - if type_names.insert(ident) { - structs.insert(ident, strct); - } else { + 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(ident, strct); for field in &strct.fields { visit(&mut all, &field.ty); } } Api::Enum(enm) => { let ident = &enm.ident; - // We allow declaring the same type as a shared enum and as a Cxxtype, as this - // means not to emit the C++ enum definition. - if !type_names.insert(ident) && !cxx.contains(ident) { + 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); } Api::CxxType(ety) => { let ident = &ety.ident; - // We allow declaring the same type as a shared enum and as a Cxxtype, as this - // means not to emit the C++ enum definition. - if !type_names.insert(ident) && !enums.contains_key(ident) { + 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); From 73075fe85fcee399c9a1c7e92834af2f7b89c553 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 01:58:06 +0000 Subject: [PATCH 732/2232] Fix stray question operator --- diff --git a/syntax/file.rs b/syntax/file.rs index 2141c17..8b86adc 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -48,7 +48,7 @@ impl Parse for Module { return Err(Error::new_spanned( span, "#[cxx::bridge] module must have inline contents", - ))?; + )); } let content; From 3a45173325ea341e8ad15c5846d262be96f056cc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 01:58:06 +0000 Subject: [PATCH 733/2232] Reject unsafe on an extern Rust block error: extern "Rust" block does not need to be unsafe --> lib.rs:116:5 | 116 | unsafe extern "Rust" { | ^^^^^^^^^^^^^^^^^^^^ --- diff --git a/syntax/parse.rs b/syntax/parse.rs index c23ee8f..32a6ddf 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -171,11 +171,23 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { } fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { - let lang = match parse_lang(foreign_mod.abi) { + let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, Err(err) => return cx.push(err), }; + match lang { + Lang::Rust => { + if foreign_mod.unsafety.is_some() { + let unsafety = foreign_mod.unsafety; + let abi = foreign_mod.abi; + let span = quote!(#unsafety #abi); + cx.error(span, "extern \"Rust\" block does not need to be unsafe"); + } + } + Lang::Cxx => {} + } + let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { @@ -222,7 +234,7 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec out.extend(items); } -fn parse_lang(abi: Abi) -> Result { +fn parse_lang(abi: &Abi) -> Result { let name = match &abi.name { Some(name) => name, None => { From 00f236a0e08d28e25084b707c6f31f5b5020b32d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:10:15 +0000 Subject: [PATCH 734/2232] Keep track of whether extern type layout can be trusted --- diff --git a/syntax/mod.rs b/syntax/mod.rs index ae37802..e2356e1 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -48,6 +48,7 @@ pub struct ExternType { pub type_token: Token![type], pub ident: Ident, pub semi_token: Token![;], + pub trusted: bool, } pub struct Struct { diff --git a/syntax/parse.rs b/syntax/parse.rs index 32a6ddf..7520644 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -176,9 +176,10 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => return cx.push(err), }; + let trusted = foreign_mod.unsafety.is_some(); match lang { Lang::Rust => { - if foreign_mod.unsafety.is_some() { + if trusted { let unsafety = foreign_mod.unsafety; let abi = foreign_mod.abi; let span = quote!(#unsafety #abi); @@ -191,7 +192,7 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang) { + ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted) { Ok(ety) => items.push(ety), Err(err) => cx.push(err), }, @@ -251,7 +252,12 @@ fn parse_lang(abi: &Abi) -> Result { } } -fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang) -> Result { +fn parse_extern_type( + cx: &mut Errors, + foreign_type: &ForeignItemType, + lang: Lang, + trusted: bool, +) -> Result { let doc = attrs::parse_doc(cx, &foreign_type.attrs); let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); @@ -265,6 +271,7 @@ fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang type_token, ident, semi_token, + trusted, })) } From 805dca3e10ac26b0ed952e89db908a54a5207f2b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:11:02 +0000 Subject: [PATCH 735/2232] Propagate unsafety from module to extern block --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index fa9e407..68c6f85 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -62,7 +62,8 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { .next() .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.content); + let trusted = bridge.unsafety.is_some(); + let ref apis = syntax::parse_items(errors, bridge.content, trusted); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index aecec75..9cfe3b5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -14,7 +14,8 @@ use syn::{parse_quote, Result, Token}; pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); - let ref apis = syntax::parse_items(errors, content); + let trusted = ffi.unsafety.is_some(); + let ref apis = syntax::parse_items(errors, content, trusted); let ref types = Types::collect(errors, apis); errors.propagate()?; let namespace = &ffi.namespace; diff --git a/syntax/parse.rs b/syntax/parse.rs index 7520644..415b802 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -20,7 +20,7 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { +pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec { let mut apis = Vec::new(); for item in items { match item { @@ -32,7 +32,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, - Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), + Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), Item::Other(item) => cx.error(item, "unsupported item"), } @@ -170,16 +170,20 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { })) } -fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec) { +fn parse_foreign_mod( + cx: &mut Errors, + foreign_mod: ItemForeignMod, + out: &mut Vec, + trusted: bool, +) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, Err(err) => return cx.push(err), }; - let trusted = foreign_mod.unsafety.is_some(); match lang { Lang::Rust => { - if trusted { + if foreign_mod.unsafety.is_some() { let unsafety = foreign_mod.unsafety; let abi = foreign_mod.abi; let span = quote!(#unsafety #abi); @@ -189,6 +193,8 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Lang::Cxx => {} } + let trusted = trusted || foreign_mod.unsafety.is_some(); + let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { From 45f3f89e4318c8193780a0e88be6356f30587b4f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:19:56 +0000 Subject: [PATCH 736/2232] Write OrderedSet using reference syntax --- diff --git a/syntax/set.rs b/syntax/set.rs index 688d1c0..de13088 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -2,12 +2,12 @@ use std::collections::HashSet; use std::hash::Hash; use std::slice; -pub struct OrderedSet<'a, T> { - set: HashSet<&'a T>, - vec: Vec<&'a T>, +pub struct OrderedSet { + set: HashSet, + vec: Vec, } -impl<'a, T> OrderedSet<'a, T> +impl<'a, T> OrderedSet<&'a T> where T: Hash + Eq, { @@ -31,7 +31,7 @@ where } } -impl<'s, 'a, T> IntoIterator for &'s OrderedSet<'a, T> { +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 { diff --git a/syntax/types.rs b/syntax/types.rs index d579c78..ff3d049 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -7,11 +7,11 @@ use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; pub struct Types<'a> { - pub all: Set<'a, Type>, + pub all: Set<&'a Type>, pub structs: Map<&'a Ident, &'a Struct>, pub enums: Map<&'a Ident, &'a Enum>, - pub cxx: Set<'a, Ident>, - pub rust: Set<'a, Ident>, + pub cxx: Set<&'a Ident>, + pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, } @@ -24,7 +24,7 @@ impl<'a> Types<'a> { let mut rust = Set::new(); let mut aliases = Map::new(); - fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { + fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); match ty { Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {} From da219b38e4409bff01e2701423d8dcb3aefcd0a9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:21:49 +0000 Subject: [PATCH 737/2232] Track set of trusted extern types --- diff --git a/syntax/types.rs b/syntax/types.rs index ff3d049..533ea48 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -13,6 +13,7 @@ pub struct Types<'a> { pub cxx: Set<&'a Ident>, pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, + pub trusted: Set<&'a Ident>, } impl<'a> Types<'a> { @@ -23,6 +24,7 @@ impl<'a> Types<'a> { let mut cxx = Set::new(); let mut rust = Set::new(); let mut aliases = Map::new(); + let mut trusted = Set::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -99,6 +101,9 @@ impl<'a> Types<'a> { duplicate_name(cx, ety, ident); } cxx.insert(ident); + if ety.trusted { + trusted.insert(ident); + } } Api::RustType(ety) => { let ident = &ety.ident; @@ -137,6 +142,7 @@ impl<'a> Types<'a> { cxx, rust, aliases, + trusted, } } From c8b5035ae2d28c0198395554c6a0c6019262e2bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:28:25 +0000 Subject: [PATCH 738/2232] Enforce that extern shared structs are declared unsafe --- diff --git a/syntax/check.rs b/syntax/check.rs index 5980f75..f823964 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -167,16 +167,22 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { } fn check_api_struct(cx: &mut Check, strct: &Struct) { - check_reserved_name(cx, &strct.ident); + let ident = &strct.ident; + check_reserved_name(cx, ident); 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(&strct.ident) { - let span = span_for_struct_error(strct); - cx.error(span, "extern C++ structs are not implemented yet"); + if cx.types.cxx.contains(ident) { + if let Some(ety) = cx.types.untrusted.get(ident) { + let msg = "extern shared struct must be declared in an `unsafe extern` block"; + cx.error(ety, msg); + } else { + let span = span_for_struct_error(strct); + cx.error(span, "extern C++ structs are not implemented yet"); + } } for field in &strct.fields { diff --git a/syntax/types.rs b/syntax/types.rs index 533ea48..8a86a46 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, Struct, Type, TypeAlias}; +use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -13,7 +13,7 @@ pub struct Types<'a> { pub cxx: Set<&'a Ident>, pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, - pub trusted: Set<&'a Ident>, + pub untrusted: Map<&'a Ident, &'a ExternType>, } impl<'a> Types<'a> { @@ -24,7 +24,7 @@ impl<'a> Types<'a> { let mut cxx = Set::new(); let mut rust = Set::new(); let mut aliases = Map::new(); - let mut trusted = Set::new(); + let mut untrusted = Map::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -101,8 +101,8 @@ impl<'a> Types<'a> { duplicate_name(cx, ety, ident); } cxx.insert(ident); - if ety.trusted { - trusted.insert(ident); + if !ety.trusted { + untrusted.insert(ident, ety); } } Api::RustType(ety) => { @@ -142,7 +142,7 @@ impl<'a> Types<'a> { cxx, rust, aliases, - trusted, + untrusted, } } From a593d6e867ee66f28a4704257bc15a782df25979 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 02:48:08 +0000 Subject: [PATCH 739/2232] Implement extern C++ shared structs --- diff --git a/gen/src/write.rs b/gen/src/write.rs index a9b8dde..32ccbf6 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -62,7 +62,9 @@ pub(super) fn gen( match api { Api::Struct(strct) => { out.next_section(); - write_struct(out, strct); + if !types.cxx.contains(&strct.ident) { + write_struct(out, strct); + } } Api::Enum(enm) => { out.next_section(); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9cfe3b5..c2d88ce 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -43,7 +43,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { - if !types.enums.contains_key(&ety.ident) { + let ident = &ety.ident; + if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { expanded.extend(expand_cxx_type(namespace, ety)); } } diff --git a/syntax/check.rs b/syntax/check.rs index f823964..147984e 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -179,9 +179,6 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { if let Some(ety) = cx.types.untrusted.get(ident) { let msg = "extern shared struct must be declared in an `unsafe extern` block"; cx.error(ety, msg); - } else { - let span = span_for_struct_error(strct); - cx.error(span, "extern C++ structs are not implemented yet"); } } From e1e12220dacb76fe45e79fa74706a3e09bbbe402 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:04:31 +0000 Subject: [PATCH 740/2232] Revert "Add option to omit type definitions." This reverts commit 5a6c7b534d33680606570ffe78b025c145fe6655. We'll instead use an `extern` type to indicate when a type has already been defined by C++. This matches the approach to extern enums introduced in cxx 0.3.1. mod ffi { struct TheStruct { ... } unsafe extern "C" { type TheStruct; } } --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 1c84a23..f2e5c9b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -34,8 +34,6 @@ pub struct Opt { /// Whether to set __attribute__((visibility("default"))) /// or similar annotations on function implementations. pub cxx_impl_annotations: Option, - /// Whether to omit definitions of types. - pub omit_type_definitions: bool, } /// Results of code generation. diff --git a/gen/src/write.rs b/gen/src/write.rs index b184b52..51c4b02 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -60,7 +60,7 @@ pub(super) fn gen( for api in apis { match api { - Api::Struct(strct) if !opt.omit_type_definitions => { + Api::Struct(strct) => { out.next_section(); write_struct(out, strct); } @@ -68,7 +68,7 @@ pub(super) fn gen( out.next_section(); if types.cxx.contains(&enm.ident) { check_enum(out, enm); - } else if !opt.omit_type_definitions { + } else { write_enum(out, enm); } } From 5fc28551e180471b822f47b295a76fa2d02bf649 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:14:22 +0000 Subject: [PATCH 741/2232] Merge pull request 258 from adetaylor/cxx-embedding-lib --- diff --git a/BUCK b/BUCK index c554e1f..a58e0a2 100644 --- a/BUCK +++ b/BUCK @@ -68,3 +68,17 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "lib", + srcs = glob(["gen/lib/src/**"]), + visibility = ["PUBLIC"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/BUILD b/BUILD index d5255a8..017b7cc 100644 --- a/BUILD +++ b/BUILD @@ -67,3 +67,18 @@ rust_library( "//third-party:syn", ], ) + +rust_library( + name = "lib", + srcs = glob(["gen/lib/src/**/*.rs"]), + data = ["gen/build/src/gen/include/cxx.h"], + visibility = ["//visibility:public"], + deps = [ + "//third-party:anyhow", + "//third-party:cc", + "//third-party:codespan-reporting", + "//third-party:proc-macro2", + "//third-party:quote", + "//third-party:syn", + ], +) diff --git a/Cargo.toml b/Cargo.toml index 5f20ad9..eefafff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo-rs", "flags", "gen/build", "gen/cmd", "macro", "tests/ffi"] +members = ["demo-rs", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml new file mode 100644 index 0000000..3a5d938 --- /dev/null +++ b/gen/lib/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cxx-gen" +version = "0.3.4" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "C++ code generator for integrating `cxx` crate into higher level tools." +repository = "https://github.com/dtolnay/cxx" +keywords = ["ffi"] +categories = ["development-tools::ffi"] + +[dependencies] +anyhow = "1.0" +cc = "1.0.49" +codespan-reporting = "0.9" +proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } +quote = { version = "1.0", default-features = false } +syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/LICENSE-APACHE b/gen/lib/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/gen/lib/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/gen/lib/LICENSE-MIT b/gen/lib/LICENSE-MIT new file mode 120000 index 0000000..b2cfbdc --- /dev/null +++ b/gen/lib/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT \ No newline at end of file diff --git a/gen/lib/src/gen b/gen/lib/src/gen new file mode 120000 index 0000000..929cb3d --- /dev/null +++ b/gen/lib/src/gen @@ -0,0 +1 @@ +../../src \ No newline at end of file diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs new file mode 100644 index 0000000..cecf654 --- /dev/null +++ b/gen/lib/src/lib.rs @@ -0,0 +1,46 @@ +//! The CXX code generator for constructing and compiling C++ code. +//! +//! This is intended to be embedded into higher-level code generators. + +mod gen; +mod syntax; + +pub use crate::gen::Opt; +use proc_macro2::TokenStream; + +pub use crate::gen::{Error, GeneratedCode, Result}; + +/// Generate C++ bindings code from a Rust token stream. This should be a Rust +/// token stream which somewhere contains a `#[cxx::bridge] mod {}`. +pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { + gen::do_generate_from_tokens(rust_source, opt) +} + +#[cfg(test)] +mod test { + use quote::quote; + + #[test] + fn test_positive() { + let rs = quote! { + #[cxx::bridge] + mod ffi { + extern "C" { + fn in_C(); + } + extern "Rust" { + fn in_rs(); + } + } + }; + let code = crate::generate_header_and_cc(rs).unwrap(); + assert!(code.cxx.len() > 0); + assert!(code.header.len() > 0); + } + + #[test] + fn test_negative() { + let rs = quote! {}; + assert!(crate::generate_header_and_cc(rs).is_err()) + } +} diff --git a/gen/lib/src/syntax b/gen/lib/src/syntax new file mode 120000 index 0000000..a6fe06c --- /dev/null +++ b/gen/lib/src/syntax @@ -0,0 +1 @@ +../../../syntax \ No newline at end of file diff --git a/gen/src/error.rs b/gen/src/error.rs index 537351f..9a55243 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -11,12 +11,15 @@ use std::ops::Range; use std::path::Path; use std::process; -pub(super) type Result = std::result::Result; +pub type Result = std::result::Result; #[derive(Debug)] -pub(super) enum Error { +pub enum Error { + /// No `#[cxx::bridge]` module could be found. NoBridgeMod, + /// An IO error occurred when reading Rust code. Io(io::Error), + /// A syntax error occurred when parsing Rust code. Syn(syn::Error), } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 68c6f85..a939760 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -10,15 +10,19 @@ mod write; #[cfg(test)] mod tests; -use self::error::{format_err, Error, Result}; +use self::error::format_err; +pub use self::error::{Error, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; +use proc_macro2::TokenStream; +use std::clone::Clone; use std::fs; use std::path::Path; -#[derive(Default)] -pub(super) struct Opt { +/// Options for C++ code generation. +#[derive(Default, Clone)] +pub struct Opt { /// Any additional headers to #include pub include: Vec, /// Whether to set __attribute__((visibility("default"))) @@ -26,6 +30,14 @@ pub(super) struct Opt { pub cxx_impl_annotations: Option, } +/// Results of code generation. +pub struct GeneratedCode { + /// The bytes of a C++ header file. + pub header: Vec, + /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) + pub cxx: Vec, +} + pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; generate_from_path(path, opt, header) @@ -36,26 +48,52 @@ pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { generate_from_path(path, opt, header) } +pub(super) fn do_generate_from_tokens( + tokens: TokenStream, + opt: Opt, +) -> std::result::Result { + let syntax = syn::parse2::(tokens)?; + match generate(syntax, opt, true, true) { + Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), + Err(err) => Err(err), + _ => panic!("Unexpected generation"), + } +} + fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - let mut source = source.as_str(); + match generate_from_string(&source, opt, header) { + Ok(out) => out, + Err(err) => format_err(path, &source, err), + } +} + +fn generate_from_string(source: &str, opt: Opt, header: bool) -> Result> { + let mut source = source; if source.starts_with("#!") && !source.starts_with("#![") { let shebang_end = source.find('\n').unwrap_or(source.len()); source = &source[shebang_end..]; } - match generate(source, opt, header) { - Ok(out) => out, - Err(err) => format_err(path, source, err), + let syntax: File = syn::parse_str(source)?; + let results = generate(syntax, opt, header, !header)?; + match results { + (Some(hdr), None) => Ok(hdr), + (None, Some(cxx)) => Ok(cxx), + _ => panic!("Unexpected generation"), } } -fn generate(source: &str, opt: Opt, header: bool) -> Result> { +fn generate( + syntax: File, + opt: Opt, + gen_header: bool, + gen_cxx: bool, +) -> Result<(Option>, Option>)> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); - let syntax: File = syn::parse_str(source)?; let bridge = syntax .modules .into_iter() @@ -68,6 +106,18 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; - let out = write::gen(namespace, apis, types, opt, header); - Ok(out.content()) + // Some callers may wish to generate both header and C++ + // from the same token stream to avoid parsing twice. But others + // only need to generate one or the other. + let hdr = if gen_header { + Some(write::gen(namespace, apis, types, opt.clone(), true).content()) + } else { + None + }; + let cxx = if gen_cxx { + Some(write::gen(namespace, apis, types, opt, false).content()) + } else { + None + }; + Ok((hdr, cxx)) } diff --git a/gen/src/tests.rs b/gen/src/tests.rs index 0e7a910..7621643 100644 --- a/gen/src/tests.rs +++ b/gen/src/tests.rs @@ -1,4 +1,4 @@ -use crate::gen::{generate, Opt}; +use crate::gen::{generate_from_string, Opt}; const CPP_EXAMPLE: &'static str = r#" #[cxx::bridge] @@ -15,7 +15,7 @@ fn test_cpp() { include: Vec::new(), cxx_impl_annotations: None, }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. @@ -28,7 +28,7 @@ fn test_annotation() { include: Vec::new(), cxx_impl_annotations: Some("ANNOTATION".to_string()), }; - let output = generate(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); } From 366c41a74e24d45d929e04f15d80ab1445ed8f32 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:28:21 +0000 Subject: [PATCH 742/2232] Move tokenstream-only codepath out of common dir --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index cecf654..aa60b05 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -8,12 +8,25 @@ mod syntax; pub use crate::gen::Opt; use proc_macro2::TokenStream; -pub use crate::gen::{Error, GeneratedCode, Result}; +pub use crate::gen::{Error, Result}; + +/// Results of code generation. +pub struct GeneratedCode { + /// The bytes of a C++ header file. + pub header: Vec, + /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) + pub cxx: Vec, +} /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { - gen::do_generate_from_tokens(rust_source, opt) + let syntax = syn::parse2(rust_source)?; + match gen::generate(syntax, opt, true, true) { + Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), + Err(err) => Err(err), + _ => panic!("Unexpected generation"), + } } #[cfg(test)] diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a939760..9c35285 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -15,7 +15,6 @@ pub use self::error::{Error, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; -use proc_macro2::TokenStream; use std::clone::Clone; use std::fs; use std::path::Path; @@ -30,14 +29,6 @@ pub struct Opt { pub cxx_impl_annotations: Option, } -/// Results of code generation. -pub struct GeneratedCode { - /// The bytes of a C++ header file. - pub header: Vec, - /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) - pub cxx: Vec, -} - pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { let header = false; generate_from_path(path, opt, header) @@ -48,18 +39,6 @@ pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { generate_from_path(path, opt, header) } -pub(super) fn do_generate_from_tokens( - tokens: TokenStream, - opt: Opt, -) -> std::result::Result { - let syntax = syn::parse2::(tokens)?; - match generate(syntax, opt, true, true) { - Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), - Err(err) => Err(err), - _ => panic!("Unexpected generation"), - } -} - fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, @@ -86,7 +65,7 @@ fn generate_from_string(source: &str, opt: Opt, header: bool) -> Result> } } -fn generate( +pub(super) fn generate( syntax: File, opt: Opt, gen_header: bool, From f33c871b6046253c496f32e4689c1bf8048a9d47 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:30:53 +0000 Subject: [PATCH 743/2232] Fix compilation of cxx-gen unit test --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index aa60b05..186e6dd 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -31,6 +31,7 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result 0); assert!(code.header.len() > 0); } @@ -54,6 +56,7 @@ mod test { #[test] fn test_negative() { let rs = quote! {}; - assert!(crate::generate_header_and_cc(rs).is_err()) + let opt = Opt::default(); + assert!(crate::generate_header_and_cc(rs, opt).is_err()) } } From cae428a3b28b7e92ed3ff32455621d92b1bbd528 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:32:43 +0000 Subject: [PATCH 744/2232] Move test of generated_header_and_cc to integration test This test only involves the public API of the crate. --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 186e6dd..da767fa 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -28,35 +28,3 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result panic!("Unexpected generation"), } } - -#[cfg(test)] -mod test { - use crate::Opt; - use quote::quote; - - #[test] - fn test_positive() { - let rs = quote! { - #[cxx::bridge] - mod ffi { - extern "C" { - fn in_C(); - } - extern "Rust" { - fn in_rs(); - } - } - }; - let opt = Opt::default(); - let code = crate::generate_header_and_cc(rs, opt).unwrap(); - assert!(code.cxx.len() > 0); - assert!(code.header.len() > 0); - } - - #[test] - fn test_negative() { - let rs = quote! {}; - let opt = Opt::default(); - assert!(crate::generate_header_and_cc(rs, opt).is_err()) - } -} diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs new file mode 100644 index 0000000..5c53628 --- /dev/null +++ b/gen/lib/tests/test.rs @@ -0,0 +1,28 @@ +use cxx_gen::Opt; +use quote::quote; + +#[test] +fn test_positive() { + let rs = quote! { + #[cxx::bridge] + mod ffi { + extern "C" { + fn in_C(); + } + extern "Rust" { + fn in_rs(); + } + } + }; + let opt = Opt::default(); + let code = cxx_gen::generate_header_and_cc(rs, opt).unwrap(); + assert!(code.cxx.len() > 0); + assert!(code.header.len() > 0); +} + +#[test] +fn test_negative() { + let rs = quote! {}; + let opt = Opt::default(); + assert!(cxx_gen::generate_header_and_cc(rs, opt).is_err()) +} From f854d7ee467f3b05d463294e3fe05ee2f5e22dae Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:36:31 +0000 Subject: [PATCH 745/2232] Ignore cxx-gen not using format_err --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index da767fa..934d929 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -2,6 +2,8 @@ //! //! This is intended to be embedded into higher-level code generators. +#![allow(dead_code)] + mod gen; mod syntax; From 174d30672802cef1a7056f4faff1119cc172b006 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:40:30 +0000 Subject: [PATCH 746/2232] Update POC for cxx-gen crate --- diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 3a5d938..89cf9f4 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cxx-gen" version = "0.3.4" -authors = ["David Tolnay "] +authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into higher level tools." From c3fe75d72a791df8a17c47822d24f2f7fc37a415 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:40:49 +0000 Subject: [PATCH 747/2232] Uncouple cxx-gen versioning from cxx crate --- diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 89cf9f4..348b176 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.3.4" +version = "0.0.0" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" From 1fd4813a897cc3124f21744a84d9598eb7b8a3a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:46:35 +0000 Subject: [PATCH 748/2232] Fix //:lib bazel build --- diff --git a/BUILD b/BUILD index 017b7cc..53bccf7 100644 --- a/BUILD +++ b/BUILD @@ -71,7 +71,7 @@ rust_library( rust_library( name = "lib", srcs = glob(["gen/lib/src/**/*.rs"]), - data = ["gen/build/src/gen/include/cxx.h"], + data = ["gen/lib/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ "//third-party:anyhow", From 135753b46c4b506d5df3f6578881fe2c28e753d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:48:12 +0000 Subject: [PATCH 749/2232] Include cxx-gen in third-party lockfile --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 65ec211..3c5e6ba 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -90,6 +90,18 @@ dependencies = [ ] [[package]] +name = "cxx-gen" +version = "0.0.0" +dependencies = [ + "anyhow", + "cc", + "codespan-reporting", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "cxx-test-suite" version = "0.0.0" dependencies = [ From 6f7f686f15f8629348d4309d758739d539e5b929 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 05:56:49 +0000 Subject: [PATCH 750/2232] Remove Result alias from API of cxx-gen library This is unnecessary when we don't expect downstream code to be commonly passing around cxx_gen::Result. --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 934d929..3b1b3c6 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -7,11 +7,9 @@ mod gen; mod syntax; -pub use crate::gen::Opt; +pub use crate::gen::{Error, Opt}; use proc_macro2::TokenStream; -pub use crate::gen::{Error, Result}; - /// Results of code generation. pub struct GeneratedCode { /// The bytes of a C++ header file. @@ -22,7 +20,7 @@ pub struct GeneratedCode { /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. -pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { +pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { let syntax = syn::parse2(rust_source)?; match gen::generate(syntax, opt, true, true) { Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), diff --git a/gen/src/error.rs b/gen/src/error.rs index 9a55243..70dcb37 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -11,7 +11,7 @@ use std::ops::Range; use std::path::Path; use std::process; -pub type Result = std::result::Result; +pub(crate) type Result = std::result::Result; #[derive(Debug)] pub enum Error { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 9c35285..320ff66 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -10,8 +10,8 @@ mod write; #[cfg(test)] mod tests; -use self::error::format_err; -pub use self::error::{Error, Result}; +pub use self::error::Error; +use self::error::{format_err, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; From e9c533e43031cba8f306c7492ad0902207c0f8f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:03:53 +0000 Subject: [PATCH 751/2232] Hide error enum variants from cxx-gen public api We can expose more detail on the error as the need arises, but start with an opaque error type for now. --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 3b1b3c6..ca9fabe 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -7,8 +7,10 @@ mod gen; mod syntax; -pub use crate::gen::{Error, Opt}; +pub use crate::gen::Opt; use proc_macro2::TokenStream; +use std::error::Error as StdError; +use std::fmt::{self, Debug, Display}; /// Results of code generation. pub struct GeneratedCode { @@ -18,13 +20,34 @@ pub struct GeneratedCode { pub cxx: Vec, } +pub struct Error(crate::gen::Error); + /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { - let syntax = syn::parse2(rust_source)?; - match gen::generate(syntax, opt, true, true) { - Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx }), - Err(err) => Err(err), + let syntax = syn::parse2(rust_source) + .map_err(crate::gen::Error::from) + .map_err(Error)?; + match gen::generate(syntax, opt, true, true).map_err(Error)? { + (Some(header), Some(cxx)) => Ok(GeneratedCode { header, cxx }), _ => panic!("Unexpected generation"), } } + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } +} diff --git a/gen/src/error.rs b/gen/src/error.rs index 70dcb37..1cedab1 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -14,12 +14,9 @@ use std::process; pub(crate) type Result = std::result::Result; #[derive(Debug)] -pub enum Error { - /// No `#[cxx::bridge]` module could be found. +pub(crate) enum Error { NoBridgeMod, - /// An IO error occurred when reading Rust code. Io(io::Error), - /// A syntax error occurred when parsing Rust code. Syn(syn::Error), } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 320ff66..a5a3780 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -10,7 +10,7 @@ mod write; #[cfg(test)] mod tests; -pub use self::error::Error; +pub(super) use self::error::Error; use self::error::{format_err, Result}; use self::file::File; use crate::syntax::report::Errors; From ec0881544d2c94eaa2177f9002ec97f1e5e7afba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:29:26 +0000 Subject: [PATCH 752/2232] Make code generator options non-exhaustive --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a5a3780..a5dcdd0 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -20,7 +20,21 @@ use std::fs; use std::path::Path; /// Options for C++ code generation. +/// +/// We expect options to be added over time, so this is a non-exhaustive struct. +/// To instantiate one you need to crate a default value and mutate those fields +/// that you want to modify. +/// +/// ``` +/// # use cxx_gen::Opt; +/// # +/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned(); +/// +/// let mut opt = Opt::default(); +/// opt.cxx_impl_annotations = Some(impl_annotations); +/// ``` #[derive(Default, Clone)] +#[non_exhaustive] pub struct Opt { /// Any additional headers to #include pub include: Vec, From df2f78d8bacd9941e551299f7b0b3b361d4ddfd9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:31:53 +0000 Subject: [PATCH 753/2232] Expand on field documentation of Opt struct --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a5dcdd0..5bc51e1 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -36,10 +36,15 @@ use std::path::Path; #[derive(Default, Clone)] #[non_exhaustive] pub struct Opt { - /// Any additional headers to #include + /// Any additional headers to #include. The cxxbridge tool does not parse or + /// even require the given paths to exist; they simply go into the generated + /// C++ code as #include lines. pub include: Vec, - /// Whether to set __attribute__((visibility("default"))) - /// or similar annotations on function implementations. + /// Optional annotation for implementations of C++ function wrappers that + /// may be exposed to Rust. You may for example need to provide + /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if + /// Rust code from one shared object or executable depends on these C++ + /// functions in another. pub cxx_impl_annotations: Option, } From 2719a65d2b33d3703a65356127613b85d521257d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:41:06 +0000 Subject: [PATCH 754/2232] Remove import which is already provided by prelude --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 5bc51e1..6573949 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -15,7 +15,6 @@ use self::error::{format_err, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; -use std::clone::Clone; use std::fs; use std::path::Path; From a5cca315b3571a47b0192e7c3e000020dd1cb680 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:42:34 +0000 Subject: [PATCH 755/2232] Take codegen options by reference --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 14683b6..a4fcc1d 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -102,13 +102,14 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> { - let header = gen::do_generate_header(rust_source_file, Opt::default()); + let opt = Opt::default(); + let header = gen::do_generate_header(rust_source_file, &opt); let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; fs::write(&header_path, header)?; paths::symlink_header(&header_path, rust_source_file); - let bridge = gen::do_generate_bridge(rust_source_file, Opt::default()); + let bridge = gen::do_generate_bridge(rust_source_file, &opt); let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; fs::write(&bridge_path, bridge)?; build.file(&bridge_path); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 0160913..38aab49 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -35,8 +35,8 @@ fn main() { }; match (opt.input, opt.header) { - (Some(input), true) => write(gen::do_generate_header(&input, gen)), - (Some(input), false) => write(gen::do_generate_bridge(&input, gen)), + (Some(input), true) => write(gen::do_generate_header(&input, &gen)), + (Some(input), false) => write(gen::do_generate_bridge(&input, &gen)), (None, true) => write(include::HEADER), (None, false) => unreachable!(), // enforced by required_unless } diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index ca9fabe..d2ac1ca 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -24,7 +24,7 @@ pub struct Error(crate::gen::Error); /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. -pub fn generate_header_and_cc(rust_source: TokenStream, opt: Opt) -> Result { +pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result { let syntax = syn::parse2(rust_source) .map_err(crate::gen::Error::from) .map_err(Error)?; diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 6573949..0743939 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -32,7 +32,7 @@ use std::path::Path; /// let mut opt = Opt::default(); /// opt.cxx_impl_annotations = Some(impl_annotations); /// ``` -#[derive(Default, Clone)] +#[derive(Default)] #[non_exhaustive] pub struct Opt { /// Any additional headers to #include. The cxxbridge tool does not parse or @@ -47,17 +47,17 @@ pub struct Opt { pub cxx_impl_annotations: Option, } -pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec { +pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec { let header = false; generate_from_path(path, opt, header) } -pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec { +pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec { let header = true; generate_from_path(path, opt, header) } -fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { +fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), @@ -68,7 +68,7 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { } } -fn generate_from_string(source: &str, opt: Opt, header: bool) -> Result> { +fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result> { let mut source = source; if source.starts_with("#!") && !source.starts_with("#![") { let shebang_end = source.find('\n').unwrap_or(source.len()); @@ -85,7 +85,7 @@ fn generate_from_string(source: &str, opt: Opt, header: bool) -> Result> pub(super) fn generate( syntax: File, - opt: Opt, + opt: &Opt, gen_header: bool, gen_cxx: bool, ) -> Result<(Option>, Option>)> { @@ -107,7 +107,7 @@ pub(super) fn generate( // from the same token stream to avoid parsing twice. But others // only need to generate one or the other. let hdr = if gen_header { - Some(write::gen(namespace, apis, types, opt.clone(), true).content()) + Some(write::gen(namespace, apis, types, opt, true).content()) } else { None }; diff --git a/gen/src/write.rs b/gen/src/write.rs index 32ccbf6..ccb9f61 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -11,7 +11,7 @@ pub(super) fn gen( namespace: &Namespace, apis: &[Api], types: &Types, - opt: Opt, + opt: &Opt, header: bool, ) -> OutFile { let mut out_file = OutFile::new(namespace.clone(), header); @@ -21,7 +21,7 @@ pub(super) fn gen( writeln!(out.front, "#pragma once"); } - out.include.extend(opt.include); + out.include.extend(opt.include.clone()); for api in apis { if let Api::Include(include) = api { out.include.insert(include); From 30d2419e9b184411fbf88e84fef3370f0f9c4103 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 06:50:14 +0000 Subject: [PATCH 756/2232] Get gen dir tests passing --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index c59382e..5a573f0 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -17,5 +17,8 @@ proc-macro2 = { version = "1.0.17", default-features = false, features = ["span- quote = { version = "1.0", default-features = false } syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } +[dev-dependencies] +cxx-gen = { version = "=0.0.0", path = "../lib" } + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs index 5c53628..e69c6fb 100644 --- a/gen/lib/tests/test.rs +++ b/gen/lib/tests/test.rs @@ -15,7 +15,7 @@ fn test_positive() { } }; let opt = Opt::default(); - let code = cxx_gen::generate_header_and_cc(rs, opt).unwrap(); + let code = cxx_gen::generate_header_and_cc(rs, &opt).unwrap(); assert!(code.cxx.len() > 0); assert!(code.header.len() > 0); } @@ -24,5 +24,5 @@ fn test_positive() { fn test_negative() { let rs = quote! {}; let opt = Opt::default(); - assert!(cxx_gen::generate_header_and_cc(rs, opt).is_err()) + assert!(cxx_gen::generate_header_and_cc(rs, &opt).is_err()) } diff --git a/gen/src/tests.rs b/gen/src/tests.rs index 7621643..7ef731c 100644 --- a/gen/src/tests.rs +++ b/gen/src/tests.rs @@ -1,13 +1,13 @@ use crate::gen::{generate_from_string, Opt}; -const CPP_EXAMPLE: &'static str = r#" +const CPP_EXAMPLE: &str = r#" #[cxx::bridge] mod ffi { extern "C" { pub fn do_cpp_thing(foo: &str); } } - "#; +"#; #[test] fn test_cpp() { @@ -15,7 +15,7 @@ fn test_cpp() { include: Vec::new(), cxx_impl_annotations: None, }; - let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. @@ -28,7 +28,7 @@ fn test_annotation() { include: Vec::new(), cxx_impl_annotations: Some("ANNOTATION".to_string()), }; - let output = generate_from_string(CPP_EXAMPLE, opts, false).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap(); let output = std::str::from_utf8(&output).unwrap(); assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); } From 0a46aa04e782bdc320c1a0ac3e8c20135767cbfb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:05:55 +0000 Subject: [PATCH 757/2232] Update third-party lockfile with new dev dependency --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3c5e6ba..a01ac13 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -84,6 +84,7 @@ dependencies = [ "anyhow", "cc", "codespan-reporting", + "cxx-gen", "proc-macro2", "quote", "syn", From b555c741af0c3895d02d1621d6c4918a78773247 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:05:55 +0000 Subject: [PATCH 758/2232] Run tests of all workspace crates in CI --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0ca230..a77af49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: with: toolchain: ${{matrix.rust}} - run: cargo run --manifest-path demo-rs/Cargo.toml - - run: cargo test + - run: cargo test --all msrv: name: Rust 1.42.0 From 19cb7855b265bd2d9b6ae07a0ca4884c64c27073 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:05:55 +0000 Subject: [PATCH 759/2232] Use GeneratedCode consistently as return type for pair of files --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index d2ac1ca..d707529 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -7,19 +7,11 @@ mod gen; mod syntax; -pub use crate::gen::Opt; +pub use crate::gen::{GeneratedCode, Opt}; use proc_macro2::TokenStream; use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; -/// Results of code generation. -pub struct GeneratedCode { - /// The bytes of a C++ header file. - pub header: Vec, - /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) - pub cxx: Vec, -} - pub struct Error(crate::gen::Error); /// Generate C++ bindings code from a Rust token stream. This should be a Rust @@ -28,10 +20,7 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result Ok(GeneratedCode { header, cxx }), - _ => panic!("Unexpected generation"), - } + gen::generate(syntax, opt, true, true).map_err(Error) } impl Display for Error { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 0743939..ff0f736 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -47,6 +47,14 @@ pub struct Opt { pub cxx_impl_annotations: Option, } +/// Results of code generation. +pub struct GeneratedCode { + /// The bytes of a C++ header file. + pub header: Vec, + /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) + pub cxx: Vec, +} + pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec { let header = false; generate_from_path(path, opt, header) @@ -75,12 +83,8 @@ fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result source = &source[shebang_end..]; } let syntax: File = syn::parse_str(source)?; - let results = generate(syntax, opt, header, !header)?; - match results { - (Some(hdr), None) => Ok(hdr), - (None, Some(cxx)) => Ok(cxx), - _ => panic!("Unexpected generation"), - } + let generated = generate(syntax, opt, header, !header)?; + Ok(if header { generated.header } else { generated.cxx }) } pub(super) fn generate( @@ -88,7 +92,7 @@ pub(super) fn generate( opt: &Opt, gen_header: bool, gen_cxx: bool, -) -> Result<(Option>, Option>)> { +) -> Result { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let bridge = syntax @@ -106,15 +110,16 @@ pub(super) fn generate( // Some callers may wish to generate both header and C++ // from the same token stream to avoid parsing twice. But others // only need to generate one or the other. - let hdr = if gen_header { - Some(write::gen(namespace, apis, types, opt, true).content()) - } else { - None - }; - let cxx = if gen_cxx { - Some(write::gen(namespace, apis, types, opt, false).content()) - } else { - None - }; - Ok((hdr, cxx)) + Ok(GeneratedCode { + header: if gen_header { + write::gen(namespace, apis, types, opt, true).content() + } else { + Vec::new() + }, + cxx: if gen_cxx { + write::gen(namespace, apis, types, opt, false).content() + } else { + Vec::new() + }, + }) } From d3659d8adad267d45b5e5e365d8723b91f635f04 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:12:25 +0000 Subject: [PATCH 760/2232] Use 'implementation' instead of 'cxx' to refer to source files --- diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs index e69c6fb..be2e60f 100644 --- a/gen/lib/tests/test.rs +++ b/gen/lib/tests/test.rs @@ -16,8 +16,8 @@ fn test_positive() { }; let opt = Opt::default(); let code = cxx_gen::generate_header_and_cc(rs, &opt).unwrap(); - assert!(code.cxx.len() > 0); assert!(code.header.len() > 0); + assert!(code.implementation.len() > 0); } #[test] diff --git a/gen/src/mod.rs b/gen/src/mod.rs index ff0f736..6a1354c 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -52,7 +52,7 @@ pub struct GeneratedCode { /// The bytes of a C++ header file. pub header: Vec, /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) - pub cxx: Vec, + pub implementation: Vec, } pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec { @@ -84,14 +84,18 @@ fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result } let syntax: File = syn::parse_str(source)?; let generated = generate(syntax, opt, header, !header)?; - Ok(if header { generated.header } else { generated.cxx }) + Ok(if header { + generated.header + } else { + generated.implementation + }) } pub(super) fn generate( syntax: File, opt: &Opt, gen_header: bool, - gen_cxx: bool, + gen_implementation: bool, ) -> Result { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); @@ -116,7 +120,7 @@ pub(super) fn generate( } else { Vec::new() }, - cxx: if gen_cxx { + implementation: if gen_implementation { write::gen(namespace, apis, types, opt, false).content() } else { Vec::new() From 545d2625afda4647220581cb8a076710c43c28d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:35:03 +0000 Subject: [PATCH 761/2232] Avoid compiling the ffi crate a second time in --test mode --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a77af49..16a2f99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: with: toolchain: ${{matrix.rust}} - run: cargo run --manifest-path demo-rs/Cargo.toml - - run: cargo test --all + - run: cargo test --all --exclude cxx-test-suite msrv: name: Rust 1.42.0 From 125c91dc096e008b884ff76afb72c088577db9e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:38:15 +0000 Subject: [PATCH 762/2232] Switch from deprecated --all to --workspace --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a2f99..55431b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: with: toolchain: ${{matrix.rust}} - run: cargo run --manifest-path demo-rs/Cargo.toml - - run: cargo test --all --exclude cxx-test-suite + - run: cargo test --workspace --exclude cxx-test-suite msrv: name: Rust 1.42.0 From 318c3530b49595661b7cc3f1f1973ccc746d99b6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:50:05 +0000 Subject: [PATCH 763/2232] Handwrite Opt::default to prepare for default-true fields --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 6a1354c..d21db06 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -32,7 +32,6 @@ use std::path::Path; /// let mut opt = Opt::default(); /// opt.cxx_impl_annotations = Some(impl_annotations); /// ``` -#[derive(Default)] #[non_exhaustive] pub struct Opt { /// Any additional headers to #include. The cxxbridge tool does not parse or @@ -55,6 +54,15 @@ pub struct GeneratedCode { pub implementation: Vec, } +impl Default for Opt { + fn default() -> Self { + Opt { + include: Vec::new(), + cxx_impl_annotations: None, + } + } +} + pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec { let header = false; generate_from_path(path, opt, header) From 8238d4a316496f1cf845a04a67d9819380cc81f9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 07:59:29 +0000 Subject: [PATCH 764/2232] Use Opt to control which outputs get generated --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index a4fcc1d..aabac22 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -103,16 +103,16 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); - let header = gen::do_generate_header(rust_source_file, &opt); + let generated = gen::generate_from_path(rust_source_file, &opt); + let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; - fs::write(&header_path, header)?; + fs::write(&header_path, generated.header)?; paths::symlink_header(&header_path, rust_source_file); - let bridge = gen::do_generate_bridge(rust_source_file, &opt); - let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?; - fs::write(&bridge_path, bridge)?; - build.file(&bridge_path); + let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?; + fs::write(&implementation_path, generated.implementation)?; + build.file(&implementation_path); let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); let _ = fs::create_dir_all(cxx_h.parent().unwrap()); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 38aab49..c5cc396 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -32,11 +32,13 @@ fn main() { let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, + gen_header: opt.header, + gen_implementation: !opt.header, }; match (opt.input, opt.header) { - (Some(input), true) => write(gen::do_generate_header(&input, &gen)), - (Some(input), false) => write(gen::do_generate_bridge(&input, &gen)), + (Some(input), true) => write(gen::generate_from_path(&input, &gen).header), + (Some(input), false) => write(gen::generate_from_path(&input, &gen).implementation), (None, true) => write(include::HEADER), (None, false) => unreachable!(), // enforced by required_unless } diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index d707529..7054691 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -20,7 +20,7 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result, + + pub(super) gen_header: bool, + pub(super) gen_implementation: bool, } /// Results of code generation. @@ -59,52 +62,34 @@ impl Default for Opt { Opt { include: Vec::new(), cxx_impl_annotations: None, + gen_header: true, + gen_implementation: true, } } } -pub(super) fn do_generate_bridge(path: &Path, opt: &Opt) -> Vec { - let header = false; - generate_from_path(path, opt, header) -} - -pub(super) fn do_generate_header(path: &Path, opt: &Opt) -> Vec { - let header = true; - generate_from_path(path, opt, header) -} - -fn generate_from_path(path: &Path, opt: &Opt, header: bool) -> Vec { +pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { let source = match fs::read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match generate_from_string(&source, opt, header) { + match generate_from_string(&source, opt) { Ok(out) => out, Err(err) => format_err(path, &source, err), } } -fn generate_from_string(source: &str, opt: &Opt, header: bool) -> Result> { +fn generate_from_string(source: &str, opt: &Opt) -> Result { let mut source = source; if source.starts_with("#!") && !source.starts_with("#![") { let shebang_end = source.find('\n').unwrap_or(source.len()); source = &source[shebang_end..]; } let syntax: File = syn::parse_str(source)?; - let generated = generate(syntax, opt, header, !header)?; - Ok(if header { - generated.header - } else { - generated.implementation - }) + generate(syntax, opt) } -pub(super) fn generate( - syntax: File, - opt: &Opt, - gen_header: bool, - gen_implementation: bool, -) -> Result { +pub(super) fn generate(syntax: File, opt: &Opt) -> Result { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let bridge = syntax @@ -123,12 +108,12 @@ pub(super) fn generate( // from the same token stream to avoid parsing twice. But others // only need to generate one or the other. Ok(GeneratedCode { - header: if gen_header { + header: if opt.gen_header { write::gen(namespace, apis, types, opt, true).content() } else { Vec::new() }, - implementation: if gen_implementation { + implementation: if opt.gen_implementation { write::gen(namespace, apis, types, opt, false).content() } else { Vec::new() diff --git a/gen/src/tests.rs b/gen/src/tests.rs index 7ef731c..76ff987 100644 --- a/gen/src/tests.rs +++ b/gen/src/tests.rs @@ -14,9 +14,11 @@ fn test_cpp() { let opts = Opt { include: Vec::new(), cxx_impl_annotations: None, + gen_header: false, + gen_implementation: true, }; - let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap(); - let output = std::str::from_utf8(&output).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap(); + let output = std::str::from_utf8(&output.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); @@ -27,8 +29,10 @@ fn test_annotation() { let opts = Opt { include: Vec::new(), cxx_impl_annotations: Some("ANNOTATION".to_string()), + gen_header: false, + gen_implementation: true, }; - let output = generate_from_string(CPP_EXAMPLE, &opts, false).unwrap(); - let output = std::str::from_utf8(&output).unwrap(); + let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap(); + let output = std::str::from_utf8(&output.implementation).unwrap(); assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); } From c8256aff88af893ecfdf3f224da47e3d1a2aaa76 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 08:32:41 +0000 Subject: [PATCH 765/2232] Link to some context about autocxx --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 7054691..81962e1 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -1,6 +1,11 @@ //! The CXX code generator for constructing and compiling C++ code. //! -//! This is intended to be embedded into higher-level code generators. +//! This is intended as a mechanism for embedding the `cxx` crate into +//! higher-level code generators. See [dtolnay/cxx#235] and +//! [https://github.com/google/autocxx]. +//! +//! [dtolnay/cxx#235]: https://github.com/dtolnay/cxx/issues/235 +//! [https://github.com/google/autocxx]: https://github.com/google/autocxx #![allow(dead_code)] From bf86c33570daf775b30fa9e113d8c068f1e94597 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 08:33:37 +0000 Subject: [PATCH 766/2232] Publish cxx-gen 0.0.1 --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 5a573f0..6bbc9e4 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -18,7 +18,7 @@ quote = { version = "1.0", default-features = false } syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [dev-dependencies] -cxx-gen = { version = "=0.0.0", path = "../lib" } +cxx-gen = { version = "=0.0.1", path = "../lib" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 348b176..eb17487 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.0.0" +version = "0.0.1" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index a01ac13..e9fb1ff 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.0.0" +version = "0.0.1" dependencies = [ "anyhow", "cc", From 9075cdcd38bf0c02a4c68ff9bcf2c235fd190968 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 19:10:38 +0000 Subject: [PATCH 767/2232] Release 0.3.7 --- diff --git a/Cargo.toml b/Cargo.toml index eefafff..c21f683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.6" # remember to update html_root_url +version = "0.3.7" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.3.6", path = "macro" } +cxxbridge-macro = { version = "=0.3.7", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.3.6", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.3.7", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.3.6", path = "gen/build" } +cxx-build = { version = "=0.3.7", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 6e5bc5d..7d7453b 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.3.6" +version = "0.3.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 6bbc9e4..3faed78 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.6" +version = "0.3.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 03fb4cf..fd4d31a 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.6" +version = "0.3.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 2cc2a72..6ca6182 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.6" +version = "0.3.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index ad195cc..dc26f67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.6")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.7")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index e9fb1ff..ae67969 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.6" +version = "0.3.7" dependencies = [ "cc", "cxx-build", @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.6" +version = "0.3.7" dependencies = [ "anyhow", "cc", @@ -113,7 +113,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.6" +version = "0.3.7" dependencies = [ "anyhow", "clap", @@ -133,11 +133,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.3.6" +version = "0.3.7" [[package]] name = "cxxbridge-macro" -version = "0.3.6" +version = "0.3.7" dependencies = [ "cxx", "proc-macro2", From f5c72266f7eedfaba8f0dc11cb91a6fba90fdfb1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 20:05:10 +0000 Subject: [PATCH 768/2232] Make a module for the cxx-gen error type --- diff --git a/gen/lib/src/error.rs b/gen/lib/src/error.rs new file mode 100644 index 0000000..3cf1e62 --- /dev/null +++ b/gen/lib/src/error.rs @@ -0,0 +1,25 @@ +// We can expose more detail on the error as the need arises, but start with an +// opaque error type for now. + +use std::error::Error as StdError; +use std::fmt::{self, Debug, Display}; + +pub struct Error(pub(crate) crate::gen::Error); + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } +} diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 81962e1..bdeedfc 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -9,15 +9,13 @@ #![allow(dead_code)] +mod error; mod gen; mod syntax; +pub use crate::error::Error; pub use crate::gen::{GeneratedCode, Opt}; use proc_macro2::TokenStream; -use std::error::Error as StdError; -use std::fmt::{self, Debug, Display}; - -pub struct Error(crate::gen::Error); /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. @@ -27,21 +25,3 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(&self.0, f) - } -} - -impl StdError for Error { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - self.0.source() - } -} From 507bdc1905e3739c0595e5adaa7f794339735053 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 20:07:12 +0000 Subject: [PATCH 769/2232] Store cxx-gen error as braced struct This appears more sympathetically in rustdoc as: pub struct Error { /* fields omitted */ } rather than: pub struct Error(_); --- diff --git a/gen/lib/src/error.rs b/gen/lib/src/error.rs index 3cf1e62..26249be 100644 --- a/gen/lib/src/error.rs +++ b/gen/lib/src/error.rs @@ -4,22 +4,30 @@ use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; -pub struct Error(pub(crate) crate::gen::Error); +pub struct Error { + pub(crate) err: crate::gen::Error, +} + +impl From for Error { + fn from(err: crate::gen::Error) -> Self { + Error { err } + } +} impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.0, f) + Display::fmt(&self.err, f) } } impl Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(&self.0, f) + Debug::fmt(&self.err, f) } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { - self.0.source() + self.err.source() } } diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index bdeedfc..6456200 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -22,6 +22,6 @@ use proc_macro2::TokenStream; pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result { let syntax = syn::parse2(rust_source) .map_err(crate::gen::Error::from) - .map_err(Error)?; - gen::generate(syntax, opt).map_err(Error) + .map_err(Error::from)?; + gen::generate(syntax, opt).map_err(Error::from) } From 39efd73252e3cf30e3205cef1d2d27a7aec7b988 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 20:24:03 +0000 Subject: [PATCH 770/2232] Add test of cxxbridge cli help text --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 37934e5..1092dcd 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -1,3 +1,7 @@ +#[cfg(test)] +#[path = "test.rs"] +mod test; + use super::Opt; use clap::AppSettings; use std::ffi::{OsStr, OsString}; diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs new file mode 100644 index 0000000..a9f1955 --- /dev/null +++ b/gen/cmd/src/test.rs @@ -0,0 +1,45 @@ +const EXPECTED: &str = "\ +cxxbridge 0.3.7 +David Tolnay +https://github.com/dtolnay/cxx + +USAGE: + cxxbridge .rs Emit .cc file for bridge to stdout + cxxbridge .rs --header Emit .h file for bridge to stdout + cxxbridge --header Emit rust/cxx.h header to stdout + +ARGS: + + Input Rust source file containing #[cxx::bridge]. + +OPTIONS: + --cxx-impl-annotations + Optional annotation for implementations of C++ function wrappers + that may be exposed to Rust. You may for example need to provide + __declspec(dllexport) or __attribute__((visibility(\"default\"))) + if Rust code from one shared object or executable depends on + these C++ functions in another. + + -h, --help + Print help information. + + --header + Emit header with declarations only. + + -i, --include ... + Any additional headers to #include. The cxxbridge tool does not + parse or even require the given paths to exist; they simply go + into the generated C++ code as #include lines. + + -V, --version + Print version information. +"; + +#[test] +fn test_help() { + let mut app = super::app(); + let mut out = Vec::new(); + app.write_long_help(&mut out).unwrap(); + let help = String::from_utf8(out).unwrap(); + assert_eq!(help, EXPECTED); +} From 1bf1aecda2dc024bb2892a3550862da13e391a8f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 20:24:04 +0000 Subject: [PATCH 771/2232] Work around to avoid trailing whitespace in test file --- diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index a9f1955..cecb292 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -19,7 +19,7 @@ OPTIONS: __declspec(dllexport) or __attribute__((visibility(\"default\"))) if Rust code from one shared object or executable depends on these C++ functions in another. - + \x20 -h, --help Print help information. @@ -30,7 +30,7 @@ OPTIONS: Any additional headers to #include. The cxxbridge tool does not parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. - + \x20 -V, --version Print version information. "; From 509ea2e064c0ce56edd943d3d88fb7111fb77013 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 21:25:56 +0000 Subject: [PATCH 772/2232] Hide BUCK files present in target/package/ from `buck build ...` --- diff --git a/.buckconfig b/.buckconfig index 33c15ec..fc837a1 100644 --- a/.buckconfig +++ b/.buckconfig @@ -3,6 +3,7 @@ # publish` and `cargo vendor` so this allow_symlinks setting should not be # required downstream. allow_symlinks = allow + ignore = target [cxx] cxxflags = -std=c++11 From b1ea3107e4c2e94fd39486576f5b4ad02b064df2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 30 2020 21:28:42 +0000 Subject: [PATCH 773/2232] Add note about buck project.ignore setting --- diff --git a/.buckconfig b/.buckconfig index fc837a1..6a4626f 100644 --- a/.buckconfig +++ b/.buckconfig @@ -3,6 +3,10 @@ # publish` and `cargo vendor` so this allow_symlinks setting should not be # required downstream. allow_symlinks = allow + + # Hide BUCK files under target/package/ from `buck build ...`. Otherwise: + # $ buck build ... + # //target/package/cxx-0.3.0/tests:ffi references non-existing file or directory 'target/package/cxx-0.3.0/tests/ffi/lib.rs' ignore = target [cxx] From b87d70f08c45306c5d2f28f11731a924e007c47f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 03:02:13 +0000 Subject: [PATCH 774/2232] Fix duplication of error messages Before: ``` cxxbridge: No such file or directory (os error 2) Caused by: No such file or directory (os error 2) ``` --- diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index 740ab94..c8eafa5 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -24,7 +24,7 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => Some(err), + Error::Io(err) => err.source(), _ => None, } } diff --git a/gen/src/error.rs b/gen/src/error.rs index 1cedab1..d8badac 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -33,8 +33,8 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => Some(err), - Error::Syn(err) => Some(err), + Error::Io(err) => err.source(), + Error::Syn(err) => err.source(), _ => None, } } From bb3ba50a8c3427cb2e5a4778b85f239230135119 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 03:22:41 +0000 Subject: [PATCH 775/2232] Replace anyhow dependency with a handwritten reporter --- diff --git a/BUCK b/BUCK index a58e0a2..2c60139 100644 --- a/BUCK +++ b/BUCK @@ -16,7 +16,6 @@ rust_binary( crate = "cxxbridge", visibility = ["PUBLIC"], deps = [ - "//third-party:anyhow", "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", @@ -60,7 +59,6 @@ rust_library( srcs = glob(["gen/build/src/**"]), visibility = ["PUBLIC"], deps = [ - "//third-party:anyhow", "//third-party:cc", "//third-party:codespan-reporting", "//third-party:proc-macro2", @@ -74,7 +72,6 @@ rust_library( srcs = glob(["gen/lib/src/**"]), visibility = ["PUBLIC"], deps = [ - "//third-party:anyhow", "//third-party:cc", "//third-party:codespan-reporting", "//third-party:proc-macro2", diff --git a/BUILD b/BUILD index 53bccf7..0248173 100644 --- a/BUILD +++ b/BUILD @@ -19,7 +19,6 @@ rust_binary( data = ["gen/cmd/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ - "//third-party:anyhow", "//third-party:clap", "//third-party:codespan-reporting", "//third-party:proc-macro2", @@ -59,7 +58,6 @@ rust_library( data = ["gen/build/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ - "//third-party:anyhow", "//third-party:cc", "//third-party:codespan-reporting", "//third-party:proc-macro2", @@ -74,7 +72,6 @@ rust_library( data = ["gen/lib/src/gen/include/cxx.h"], visibility = ["//visibility:public"], deps = [ - "//third-party:anyhow", "//third-party:cc", "//third-party:codespan-reporting", "//third-party:proc-macro2", diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 3faed78..9858005 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -10,7 +10,6 @@ keywords = ["ffi"] categories = ["development-tools::ffi"] [dependencies] -anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index aabac22..840424e 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -58,8 +58,8 @@ mod paths; mod syntax; use crate::error::Result; +use crate::gen::error::report; use crate::gen::Opt; -use anyhow::anyhow; use std::fs; use std::io::{self, Write}; use std::iter; @@ -93,7 +93,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> for path in rust_source_files { if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { - let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err)); + let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err)); process::exit(1); } } diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index fd4d31a..cd74cee 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -14,7 +14,6 @@ name = "cxxbridge" path = "src/main.rs" [dependencies] -anyhow = "1.0" clap = "2.33" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index eb17487..48a4b72 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -10,7 +10,6 @@ keywords = ["ffi"] categories = ["development-tools::ffi"] [dependencies] -anyhow = "1.0" cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } diff --git a/gen/src/error.rs b/gen/src/error.rs index d8badac..a7ce6e1 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -1,5 +1,4 @@ use crate::syntax; -use anyhow::anyhow; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor}; @@ -63,11 +62,34 @@ pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { display_syn_error(stderr, path, source, error); } } - _ => eprintln!("cxxbridge: {:?}", anyhow!(error)), + _ => { + let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error)); + } } process::exit(1); } +pub(crate) fn report(error: impl StdError) -> impl Display { + struct Report(E); + + impl Display for Report { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, formatter)?; + let mut error: &dyn StdError = &self.0; + + while let Some(cause) = error.source() { + formatter.write_str("\n\nCaused by:\n ")?; + Display::fmt(cause, formatter)?; + error = cause; + } + + Ok(()) + } + } + + Report(error) +} + fn sort_syn_errors(error: syn::Error) -> Vec { let mut errors: Vec<_> = error.into_iter().collect(); errors.sort_by_key(|e| { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a832280..873ce1d 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,7 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. -mod error; +pub(super) mod error; mod file; pub(super) mod include; pub(super) mod out; diff --git a/third-party/BUCK b/third-party/BUCK index 585dafd..95c879a 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -1,13 +1,6 @@ # To be generated by Facebook's `reindeer` tool once that is open source. rust_library( - name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.32/src/**"]), - visibility = ["PUBLIC"], - features = ["std"], -) - -rust_library( name = "bitflags", srcs = glob(["vendor/bitflags-1.2.1/src/**"]), ) diff --git a/third-party/BUILD b/third-party/BUILD index 65edf92..081737e 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -6,13 +6,6 @@ load( load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") rust_library( - name = "anyhow", - srcs = glob(["vendor/anyhow-1.0.32/src/**"]), - crate_features = ["std"], - visibility = ["//visibility:public"], -) - -rust_library( name = "bitflags", srcs = glob(["vendor/bitflags-1.2.1/src/**"]), ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index ae67969..6e7a504 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -10,12 +10,6 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" - -[[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -81,7 +75,6 @@ dependencies = [ name = "cxx-build" version = "0.3.7" dependencies = [ - "anyhow", "cc", "codespan-reporting", "cxx-gen", @@ -94,7 +87,6 @@ dependencies = [ name = "cxx-gen" version = "0.0.1" dependencies = [ - "anyhow", "cc", "codespan-reporting", "proc-macro2", @@ -115,7 +107,6 @@ dependencies = [ name = "cxxbridge-cmd" version = "0.3.7" dependencies = [ - "anyhow", "clap", "codespan-reporting", "proc-macro2", From 0d85ccdf9418904364f7fd97af20002080a59a96 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 03:50:37 +0000 Subject: [PATCH 776/2232] Add context to i/o errors --- diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index c8eafa5..e1bfff2 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,6 +1,6 @@ +use crate::gen::fs; use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; pub(super) type Result = std::result::Result; @@ -8,7 +8,7 @@ pub(super) type Result = std::result::Result; pub(super) enum Error { MissingOutDir, TargetDir, - Io(io::Error), + Fs(fs::Error), } impl Display for Error { @@ -16,7 +16,7 @@ impl Display for Error { match self { Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), Error::TargetDir => write!(f, "failed to locate target dir"), - Error::Io(err) => err.fmt(f), + Error::Fs(err) => err.fmt(f), } } } @@ -24,14 +24,14 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => err.source(), + Error::Fs(err) => err.source(), _ => None, } } } -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) +impl From for Error { + fn from(err: fs::Error) -> Self { + Error::Fs(err) } } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 840424e..8f48f20 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,8 +59,7 @@ mod syntax; use crate::error::Result; use crate::gen::error::report; -use crate::gen::Opt; -use std::fs; +use crate::gen::{fs, Opt}; use std::io::{self, Write}; use std::iter; use std::path::Path; diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index ca183d9..7c572ec 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,6 +1,6 @@ use crate::error::{Error, Result}; +use crate::gen::fs; use std::env; -use std::fs; use std::path::{Path, PathBuf}; fn out_dir() -> Result { @@ -94,23 +94,21 @@ fn canonicalize(path: impl AsRef) -> Result { // unable to handle in includes. Use a poor approximation instead. // https://github.com/rust-lang/rust/issues/42869 // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(env::current_dir()?.join(path)) + Ok(fs::current_dir()?.join(path)) } #[cfg(unix)] -use std::os::unix::fs::symlink as symlink_or_copy; +use self::fs::symlink as symlink_or_copy; #[cfg(windows)] fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { - use std::os::windows::fs::symlink_file; - // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. - if symlink_file(src, dst).is_err() { + if fs::symlink_file(src, dst).is_err() { fs::copy(src, dst)?; } Ok(()) } #[cfg(not(any(unix, windows)))] -use std::fs::copy as symlink_or_copy; +use self::fs::copy as symlink_or_copy; diff --git a/gen/src/error.rs b/gen/src/error.rs index a7ce6e1..f140577 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -1,3 +1,4 @@ +use crate::gen::fs; use crate::syntax; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; @@ -15,7 +16,7 @@ pub(crate) type Result = std::result::Result; #[derive(Debug)] pub(crate) enum Error { NoBridgeMod, - Io(io::Error), + Fs(fs::Error), Syn(syn::Error), } @@ -23,7 +24,7 @@ impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::Io(err) => err.fmt(f), + Error::Fs(err) => err.fmt(f), Error::Syn(err) => err.fmt(f), } } @@ -32,16 +33,16 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => err.source(), + Error::Fs(err) => err.source(), Error::Syn(err) => err.source(), _ => None, } } } -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) +impl From for Error { + fn from(err: fs::Error) -> Self { + Error::Fs(err) } } diff --git a/gen/src/fs.rs b/gen/src/fs.rs new file mode 100644 index 0000000..6ad92f8 --- /dev/null +++ b/gen/src/fs.rs @@ -0,0 +1,121 @@ +#![allow(dead_code)] + +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; +use std::path::{Path, PathBuf}; + +type Result = std::result::Result; + +#[derive(Debug)] +pub(crate) struct Error { + source: io::Error, + message: String, +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&self.message) + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } +} + +macro_rules! err { + ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => { + Err(Error { + source: $io_error, + message: format!($fmt $(, $path.display())*), + }) + } +} + +pub(crate) fn canonicalize(path: impl AsRef) -> Result { + let path = path.as_ref(); + match std::fs::canonicalize(path) { + Ok(string) => Ok(string), + Err(e) => err!(e, "Unable to canonicalize path: `{}`", path), + } +} + +pub(crate) fn copy(from: impl AsRef, to: impl AsRef) -> Result { + let from = from.as_ref(); + let to = to.as_ref(); + match std::fs::copy(from, to) { + Ok(n) => Ok(n), + Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to), + } +} + +pub(crate) fn create_dir_all(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + match std::fs::create_dir_all(path) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to create directory `{}`", path), + } +} + +pub(crate) fn current_dir() -> Result { + match std::env::current_dir() { + Ok(dir) => Ok(dir), + Err(e) => err!(e, "Failed to determine current directory"), + } +} + +pub(crate) fn read_to_string(path: impl AsRef) -> Result { + let path = path.as_ref(); + match std::fs::read_to_string(path) { + Ok(string) => Ok(string), + Err(e) => err!(e, "Failed to read file `{}`", path), + } +} + +pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + match std::fs::remove_file(path) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to remove file `{}`", path), + } +} + +#[cfg(unix)] +pub(crate) fn symlink(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + match std::os::unix::fs::symlink(src, dst) { + Ok(()) => Ok(()), + Err(e) => err!( + e, + "Failed to create symlink `{}` pointing to `{}`", + dst, + src, + ), + } +} + +#[cfg(windows)] +pub(crate) fn symlink_file(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + match std::os::windows::fs::symlink_file(src, dst) { + Ok(()) => Ok(()), + Err(e) => err!( + e, + "Failed to create symlink `{}` pointing to `{}`", + dst, + src, + ), + } +} + +pub(crate) fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { + let path = path.as_ref(); + match std::fs::write(path, contents) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to write file `{}`", path), + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 873ce1d..c52d273 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -3,6 +3,7 @@ pub(super) mod error; mod file; +pub(super) mod fs; pub(super) mod include; pub(super) mod out; mod write; @@ -15,7 +16,6 @@ use self::error::{format_err, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; -use std::fs; use std::path::Path; /// Options for C++ code generation. @@ -71,7 +71,7 @@ impl Default for Opt { pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { let source = match fs::read_to_string(path) { Ok(source) => source, - Err(err) => format_err(path, "", Error::Io(err)), + Err(err) => format_err(path, "", Error::Fs(err)), }; match generate_from_string(&source, opt) { Ok(out) => out, From d150c13e8af3d848e796063c6197b53b319b3161 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 03:57:21 +0000 Subject: [PATCH 777/2232] Merge pull request #272 from dtolnay/context Add context to i/o errors --- diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index c8eafa5..e1bfff2 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,6 +1,6 @@ +use crate::gen::fs; use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; pub(super) type Result = std::result::Result; @@ -8,7 +8,7 @@ pub(super) type Result = std::result::Result; pub(super) enum Error { MissingOutDir, TargetDir, - Io(io::Error), + Fs(fs::Error), } impl Display for Error { @@ -16,7 +16,7 @@ impl Display for Error { match self { Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), Error::TargetDir => write!(f, "failed to locate target dir"), - Error::Io(err) => err.fmt(f), + Error::Fs(err) => err.fmt(f), } } } @@ -24,14 +24,14 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => err.source(), + Error::Fs(err) => err.source(), _ => None, } } } -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) +impl From for Error { + fn from(err: fs::Error) -> Self { + Error::Fs(err) } } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 840424e..8f48f20 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,8 +59,7 @@ mod syntax; use crate::error::Result; use crate::gen::error::report; -use crate::gen::Opt; -use std::fs; +use crate::gen::{fs, Opt}; use std::io::{self, Write}; use std::iter; use std::path::Path; diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index ca183d9..7c572ec 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,6 +1,6 @@ use crate::error::{Error, Result}; +use crate::gen::fs; use std::env; -use std::fs; use std::path::{Path, PathBuf}; fn out_dir() -> Result { @@ -94,23 +94,21 @@ fn canonicalize(path: impl AsRef) -> Result { // unable to handle in includes. Use a poor approximation instead. // https://github.com/rust-lang/rust/issues/42869 // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(env::current_dir()?.join(path)) + Ok(fs::current_dir()?.join(path)) } #[cfg(unix)] -use std::os::unix::fs::symlink as symlink_or_copy; +use self::fs::symlink as symlink_or_copy; #[cfg(windows)] fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { - use std::os::windows::fs::symlink_file; - // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. - if symlink_file(src, dst).is_err() { + if fs::symlink_file(src, dst).is_err() { fs::copy(src, dst)?; } Ok(()) } #[cfg(not(any(unix, windows)))] -use std::fs::copy as symlink_or_copy; +use self::fs::copy as symlink_or_copy; diff --git a/gen/src/error.rs b/gen/src/error.rs index a7ce6e1..f140577 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -1,3 +1,4 @@ +use crate::gen::fs; use crate::syntax; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; @@ -15,7 +16,7 @@ pub(crate) type Result = std::result::Result; #[derive(Debug)] pub(crate) enum Error { NoBridgeMod, - Io(io::Error), + Fs(fs::Error), Syn(syn::Error), } @@ -23,7 +24,7 @@ impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::Io(err) => err.fmt(f), + Error::Fs(err) => err.fmt(f), Error::Syn(err) => err.fmt(f), } } @@ -32,16 +33,16 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::Io(err) => err.source(), + Error::Fs(err) => err.source(), Error::Syn(err) => err.source(), _ => None, } } } -impl From for Error { - fn from(err: io::Error) -> Self { - Error::Io(err) +impl From for Error { + fn from(err: fs::Error) -> Self { + Error::Fs(err) } } diff --git a/gen/src/fs.rs b/gen/src/fs.rs new file mode 100644 index 0000000..6ad92f8 --- /dev/null +++ b/gen/src/fs.rs @@ -0,0 +1,121 @@ +#![allow(dead_code)] + +use std::error::Error as StdError; +use std::fmt::{self, Display}; +use std::io; +use std::path::{Path, PathBuf}; + +type Result = std::result::Result; + +#[derive(Debug)] +pub(crate) struct Error { + source: io::Error, + message: String, +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&self.message) + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.source) + } +} + +macro_rules! err { + ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => { + Err(Error { + source: $io_error, + message: format!($fmt $(, $path.display())*), + }) + } +} + +pub(crate) fn canonicalize(path: impl AsRef) -> Result { + let path = path.as_ref(); + match std::fs::canonicalize(path) { + Ok(string) => Ok(string), + Err(e) => err!(e, "Unable to canonicalize path: `{}`", path), + } +} + +pub(crate) fn copy(from: impl AsRef, to: impl AsRef) -> Result { + let from = from.as_ref(); + let to = to.as_ref(); + match std::fs::copy(from, to) { + Ok(n) => Ok(n), + Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to), + } +} + +pub(crate) fn create_dir_all(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + match std::fs::create_dir_all(path) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to create directory `{}`", path), + } +} + +pub(crate) fn current_dir() -> Result { + match std::env::current_dir() { + Ok(dir) => Ok(dir), + Err(e) => err!(e, "Failed to determine current directory"), + } +} + +pub(crate) fn read_to_string(path: impl AsRef) -> Result { + let path = path.as_ref(); + match std::fs::read_to_string(path) { + Ok(string) => Ok(string), + Err(e) => err!(e, "Failed to read file `{}`", path), + } +} + +pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + match std::fs::remove_file(path) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to remove file `{}`", path), + } +} + +#[cfg(unix)] +pub(crate) fn symlink(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + match std::os::unix::fs::symlink(src, dst) { + Ok(()) => Ok(()), + Err(e) => err!( + e, + "Failed to create symlink `{}` pointing to `{}`", + dst, + src, + ), + } +} + +#[cfg(windows)] +pub(crate) fn symlink_file(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + match std::os::windows::fs::symlink_file(src, dst) { + Ok(()) => Ok(()), + Err(e) => err!( + e, + "Failed to create symlink `{}` pointing to `{}`", + dst, + src, + ), + } +} + +pub(crate) fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { + let path = path.as_ref(); + match std::fs::write(path, contents) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to write file `{}`", path), + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 873ce1d..c52d273 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -3,6 +3,7 @@ pub(super) mod error; mod file; +pub(super) mod fs; pub(super) mod include; pub(super) mod out; mod write; @@ -15,7 +16,6 @@ use self::error::{format_err, Result}; use self::file::File; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; -use std::fs; use std::path::Path; /// Options for C++ code generation. @@ -71,7 +71,7 @@ impl Default for Opt { pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { let source = match fs::read_to_string(path) { Ok(source) => source, - Err(err) => format_err(path, "", Error::Io(err)), + Err(err) => format_err(path, "", Error::Fs(err)), }; match generate_from_string(&source, opt) { Ok(out) => out, From 585a9fe8d4d3a89e5c05ee4b5f02d3746ca03fca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 04:03:38 +0000 Subject: [PATCH 778/2232] Remove link-cplusplus dependency from non-Cargo builds --- diff --git a/BUCK b/BUCK index 2c60139..0dfa11b 100644 --- a/BUCK +++ b/BUCK @@ -6,7 +6,6 @@ rust_library( deps = [ ":core", ":macro", - "//third-party:link-cplusplus", ], ) diff --git a/BUILD b/BUILD index 0248173..27c6931 100644 --- a/BUILD +++ b/BUILD @@ -7,10 +7,7 @@ rust_library( ":cxxbridge-macro", ], visibility = ["//visibility:public"], - deps = [ - ":core-lib", - "//third-party:link-cplusplus", - ], + deps = [":core-lib"], ) rust_binary( diff --git a/build.rs b/build.rs index 2aad99f..cecf0e5 100644 --- a/build.rs +++ b/build.rs @@ -7,4 +7,5 @@ fn main() { .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); + println!("cargo:rustc-cfg=built_with_cargo"); } diff --git a/src/lib.rs b/src/lib.rs index dc26f67..4053c85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -368,6 +368,7 @@ clippy::useless_let_if_seq )] +#[cfg(built_with_cargo)] extern crate link_cplusplus; #[macro_use] diff --git a/third-party/BUCK b/third-party/BUCK index 95c879a..86e8e72 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -39,12 +39,6 @@ rust_library( ) rust_library( - name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), - visibility = ["PUBLIC"], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), visibility = ["PUBLIC"], diff --git a/third-party/BUILD b/third-party/BUILD index 081737e..7ceec9b 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -44,12 +44,6 @@ rust_library( ) rust_library( - name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), - visibility = ["//visibility:public"], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), crate_features = [ From bc7fd41907d7b17d37eef03dc6c60b192cf3a455 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 04:10:51 +0000 Subject: [PATCH 779/2232] Merge pull request #273 from dtolnay/built Remove link-cplusplus dependency from non-Cargo builds --- diff --git a/BUCK b/BUCK index 2c60139..0dfa11b 100644 --- a/BUCK +++ b/BUCK @@ -6,7 +6,6 @@ rust_library( deps = [ ":core", ":macro", - "//third-party:link-cplusplus", ], ) diff --git a/BUILD b/BUILD index 0248173..27c6931 100644 --- a/BUILD +++ b/BUILD @@ -7,10 +7,7 @@ rust_library( ":cxxbridge-macro", ], visibility = ["//visibility:public"], - deps = [ - ":core-lib", - "//third-party:link-cplusplus", - ], + deps = [":core-lib"], ) rust_binary( diff --git a/build.rs b/build.rs index 2aad99f..cecf0e5 100644 --- a/build.rs +++ b/build.rs @@ -7,4 +7,5 @@ fn main() { .compile("cxxbridge03"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); + println!("cargo:rustc-cfg=built_with_cargo"); } diff --git a/src/lib.rs b/src/lib.rs index dc26f67..4053c85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -368,6 +368,7 @@ clippy::useless_let_if_seq )] +#[cfg(built_with_cargo)] extern crate link_cplusplus; #[macro_use] diff --git a/third-party/BUCK b/third-party/BUCK index 95c879a..86e8e72 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -39,12 +39,6 @@ rust_library( ) rust_library( - name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), - visibility = ["PUBLIC"], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), visibility = ["PUBLIC"], diff --git a/third-party/BUILD b/third-party/BUILD index 081737e..7ceec9b 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -44,12 +44,6 @@ rust_library( ) rust_library( - name = "link-cplusplus", - srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]), - visibility = ["//visibility:public"], -) - -rust_library( name = "proc-macro2", srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), crate_features = [ From f574c5ed0fdb17cb9ed759760ec3fba554e334cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 07:22:28 +0000 Subject: [PATCH 780/2232] Import cc::Build The lack of import here was left over from cxx 0.2 where this code was part of a distinct cxx::Build type. Now that the entry point to cxx-build is a function rather than a Build type, we are free to import cc's Build. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 8f48f20..c7d452f 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -60,6 +60,7 @@ mod syntax; use crate::error::Result; use crate::gen::error::report; use crate::gen::{fs, Opt}; +use cc::Build; use std::io::{self, Write}; use std::iter; use std::path::Path; @@ -71,7 +72,7 @@ use std::process; /// /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile #[must_use] -pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { +pub fn bridge(rust_source_file: impl AsRef) -> Build { bridges(iter::once(rust_source_file)) } @@ -85,7 +86,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> cc::Build { /// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` -pub fn bridges(rust_source_files: impl IntoIterator>) -> cc::Build { +pub fn bridges(rust_source_files: impl IntoIterator>) -> Build { let mut build = paths::cc_build(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate @@ -100,7 +101,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> build } -fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> { +fn try_generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); From 27991968aa1cbf221edbdf8221270b0050e29028 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 07:25:05 +0000 Subject: [PATCH 781/2232] Factor out Result-returning logic of cxx-build This arrangement makes it easy to insert fallible steps into `build` which will be able to use `?` operator. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index c7d452f..8d2e519 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -87,18 +87,23 @@ pub fn bridge(rust_source_file: impl AsRef) -> Build { /// .compile("cxxbridge-demo"); /// ``` pub fn bridges(rust_source_files: impl IntoIterator>) -> Build { + let ref mut rust_source_files = rust_source_files.into_iter(); + build(rust_source_files).unwrap_or_else(|err| { + let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err)); + process::exit(1); + }) +} + +fn build(rust_source_files: &mut dyn Iterator>) -> Result { let mut build = paths::cc_build(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate for path in rust_source_files { - if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) { - let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err)); - process::exit(1); - } + try_generate_bridge(&mut build, path.as_ref())?; } - build + Ok(build) } fn try_generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { From f7c0426a9fa8580c2e6700f5725a3c3f11013fda Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 07:28:54 +0000 Subject: [PATCH 782/2232] Write rust/cxx.h header only once --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 8d2e519..afbcbe1 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -98,15 +98,24 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul let mut build = paths::cc_build(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate + write_header()?; for path in rust_source_files { - try_generate_bridge(&mut build, path.as_ref())?; + generate_bridge(&mut build, path.as_ref())?; } Ok(build) } -fn try_generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { +fn write_header() -> Result<()> { + let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); + let _ = fs::create_dir_all(cxx_h.parent().unwrap()); + let _ = fs::remove_file(cxx_h); + let _ = fs::write(cxx_h, gen::include::HEADER); + Ok(()) +} + +fn generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); @@ -118,11 +127,5 @@ fn try_generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?; fs::write(&implementation_path, generated.implementation)?; build.file(&implementation_path); - - let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); - let _ = fs::create_dir_all(cxx_h.parent().unwrap()); - let _ = fs::remove_file(cxx_h); - let _ = fs::write(cxx_h, gen::include::HEADER); - Ok(()) } From dbff3c4dc24466a4870e40fdad9cb83bb254c2a1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 07:58:02 +0000 Subject: [PATCH 783/2232] Handle non-utf8 input file with better error --- diff --git a/gen/src/error.rs b/gen/src/error.rs index f140577..b269286 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -8,8 +8,9 @@ use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io::{self, Write}; use std::ops::Range; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process; +use std::str::Utf8Error; pub(crate) type Result = std::result::Result; @@ -17,6 +18,7 @@ pub(crate) type Result = std::result::Result; pub(crate) enum Error { NoBridgeMod, Fs(fs::Error), + Utf8(PathBuf, Utf8Error), Syn(syn::Error), } @@ -25,6 +27,7 @@ impl Display for Error { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), Error::Fs(err) => err.fmt(f), + Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()), Error::Syn(err) => err.fmt(f), } } @@ -34,6 +37,7 @@ impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { Error::Fs(err) => err.source(), + Error::Utf8(_, err) => Some(err), Error::Syn(err) => err.source(), _ => None, } diff --git a/gen/src/fs.rs b/gen/src/fs.rs index 6ad92f8..d1b0b70 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -66,9 +66,9 @@ pub(crate) fn current_dir() -> Result { } } -pub(crate) fn read_to_string(path: impl AsRef) -> Result { +pub(crate) fn read(path: impl AsRef) -> Result> { let path = path.as_ref(); - match std::fs::read_to_string(path) { + match std::fs::read(path) { Ok(string) => Ok(string), Err(e) => err!(e, "Failed to read file `{}`", path), } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index c52d273..f4d643d 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -69,9 +69,9 @@ impl Default for Opt { } pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { - let source = match fs::read_to_string(path) { + let source = match read_to_string(path) { Ok(source) => source, - Err(err) => format_err(path, "", Error::Fs(err)), + Err(err) => format_err(path, "", err), }; match generate_from_string(&source, opt) { Ok(out) => out, @@ -79,6 +79,14 @@ pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { } } +fn read_to_string(path: &Path) -> Result { + let bytes = fs::read(path)?; + match String::from_utf8(bytes) { + Ok(string) => Ok(string), + Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())), + } +} + fn generate_from_string(source: &str, opt: &Opt) -> Result { let mut source = source; if source.starts_with("#!") && !source.starts_with("#![") { From ad3dbdc535c7195c54fa0d5eca325135da9149bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 08:01:39 +0000 Subject: [PATCH 784/2232] Avoid bumping modified time of existing up to date files --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index afbcbe1..001f346 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -109,9 +109,7 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul fn write_header() -> Result<()> { let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); - let _ = fs::create_dir_all(cxx_h.parent().unwrap()); - let _ = fs::remove_file(cxx_h); - let _ = fs::write(cxx_h, gen::include::HEADER); + let _ = write(cxx_h, gen::include::HEADER.as_bytes()); Ok(()) } @@ -121,11 +119,27 @@ fn generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { let header_path = paths::out_with_extension(rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; - fs::write(&header_path, generated.header)?; + write(&header_path, &generated.header)?; paths::symlink_header(&header_path, rust_source_file); let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?; - fs::write(&implementation_path, generated.implementation)?; + write(&implementation_path, &generated.implementation)?; build.file(&implementation_path); Ok(()) } + +fn write(path: &Path, content: &[u8]) -> Result<()> { + if path.exists() { + if let Ok(existing) = fs::read(path) { + if existing == content { + // Avoid bumping modified time with unchanged contents. + return Ok(()); + } + } + let _ = fs::remove_file(path); + } else { + let _ = fs::create_dir_all(path.parent().unwrap()); + } + fs::write(path, content)?; + Ok(()) +} From d300109c6f3875367457c01ba7fcabcc0203ac93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Aug 31 2020 08:05:57 +0000 Subject: [PATCH 785/2232] Update bazel build to rustc 1.46 --- diff --git a/WORKSPACE b/WORKSPACE index 08e7c80..5e0cf9e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,13 +24,13 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_45_linux", + name = "rust_1_46_linux", exec_triple = "x86_64-unknown-linux-gnu", - version = "1.45.0", + version = "1.46.0", ) rust_repository_set( - name = "rust_1_45_darwin", + name = "rust_1_46_darwin", exec_triple = "x86_64-apple-darwin", - version = "1.45.0", + version = "1.46.0", ) From 1de699da146b4ebfd376ed572bc7710809ba3870 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 01 2020 03:29:06 +0000 Subject: [PATCH 786/2232] Treat cxx_build::bridges as must_use, same as ::bridge --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 001f346..1b4bbaa 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -86,6 +86,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> Build { /// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` +#[must_use] pub fn bridges(rust_source_files: impl IntoIterator>) -> Build { let ref mut rust_source_files = rust_source_files.into_iter(); build(rust_source_files).unwrap_or_else(|err| { From 9f1e3d76a366acf2ead31a43256766bfcef5352f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 01 2020 19:40:51 +0000 Subject: [PATCH 787/2232] Release 0.3.8 --- diff --git a/Cargo.toml b/Cargo.toml index c21f683..5458398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.7" # remember to update html_root_url +version = "0.3.8" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.3.7", path = "macro" } +cxxbridge-macro = { version = "=0.3.8", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.3.7", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.3.8", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.3.7", path = "gen/build" } +cxx-build = { version = "=0.3.8", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 7d7453b..061237a 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.3.7" +version = "0.3.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 9858005..2e536f7 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.7" +version = "0.3.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index cd74cee..a49e22f 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.7" +version = "0.3.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 6ca6182..aabd4d1 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.7" +version = "0.3.8" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 4053c85..a707338 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.7")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.8")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 6e7a504..268c9f4 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.7" +version = "0.3.8" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.7" +version = "0.3.8" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.7" +version = "0.3.8" dependencies = [ "clap", "codespan-reporting", @@ -124,11 +124,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.3.7" +version = "0.3.8" [[package]] name = "cxxbridge-macro" -version = "0.3.7" +version = "0.3.8" dependencies = [ "cxx", "proc-macro2", From 8eb3dcef466c74d8dfb42e74d8f8f8b624f1bb0b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 01 2020 20:02:08 +0000 Subject: [PATCH 788/2232] Remove version number from cmd help test --- diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index cecb292..f119189 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -1,5 +1,5 @@ const EXPECTED: &str = "\ -cxxbridge 0.3.7 +cxxbridge $VERSION David Tolnay https://github.com/dtolnay/cxx @@ -41,5 +41,7 @@ fn test_help() { let mut out = Vec::new(); app.write_long_help(&mut out).unwrap(); let help = String::from_utf8(out).unwrap(); - assert_eq!(help, EXPECTED); + let version = option_env!("CARGO_PKG_VERSION").unwrap_or_default(); + let expected = EXPECTED.replace("$VERSION", version); + assert_eq!(help, expected); } From f48e97e55a31de50b4c0b455db48bbcc7554b9fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 01 2020 21:17:17 +0000 Subject: [PATCH 789/2232] Use correct target dir determined by cargo --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs new file mode 100644 index 0000000..91f229d --- /dev/null +++ b/gen/build/src/cargo.rs @@ -0,0 +1,30 @@ +use crate::error::TargetDirError; +use std::path::PathBuf; +use std::process::Command; +use std::str; + +pub(crate) fn target_dir() -> Result { + let cargo = option_env!("CARGO").unwrap_or("cargo"); + let output = Command::new(cargo) + .arg("metadata") + .arg("--no-deps") + .arg("--format-version=1") + .output() + .map_err(TargetDirError::Io)?; + + (|| { + // Cargo only outputs utf8 encoded JSON. + let mut metadata = str::from_utf8(&output.stdout).ok()?; + + let key_pattern = "\"target_directory\":"; + let key_index = metadata.rfind(key_pattern)?; + metadata = &metadata[key_index + key_pattern.len()..]; + let open_quote_index = metadata.find('"')?; + metadata = &metadata[open_quote_index + 1..]; + let close_quote_index = metadata.find('"')?; + let string = &metadata[..close_quote_index]; + let target_directory = string.replace("\\\\", "\\"); + Some(PathBuf::from(target_directory)) + })() + .ok_or(TargetDirError::NotFound) +} diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index e1bfff2..ed4c6af 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,21 +1,28 @@ use crate::gen::fs; use std::error::Error as StdError; use std::fmt::{self, Display}; +use std::io; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { MissingOutDir, - TargetDir, + TargetDir(TargetDirError), Fs(fs::Error), } +#[derive(Debug)] +pub(crate) enum TargetDirError { + Io(io::Error), + NotFound, +} + impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), - Error::TargetDir => write!(f, "failed to locate target dir"), + Error::TargetDir(_) => write!(f, "unable to identify target dir"), Error::Fs(err) => err.fmt(f), } } @@ -24,6 +31,10 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { + Error::TargetDir(err) => match err { + TargetDirError::Io(err) => Some(err), + TargetDirError::NotFound => None, + }, Error::Fs(err) => err.source(), _ => None, } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 1b4bbaa..2560aee 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -52,6 +52,7 @@ clippy::toplevel_ref_arg )] +mod cargo; mod error; mod gen; mod paths; diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 7c572ec..82cb5cc 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,3 +1,4 @@ +use crate::cargo; use crate::error::{Error, Result}; use crate::gen::fs; use std::env; @@ -72,13 +73,19 @@ pub(crate) fn include_dir() -> Result { } fn target_dir() -> Result { + let fallback_err = match cargo::target_dir() { + Ok(target_dir) => return Ok(target_dir), + Err(err) => Error::TargetDir(err), + }; + + // Fallback if Cargo did not work. let mut dir = out_dir().and_then(canonicalize)?; loop { if dir.ends_with("target") { return Ok(dir); } if !dir.pop() { - return Err(Error::TargetDir); + return Err(fallback_err); } } } From 7273963a616c0346759f0f50b7d1efb7f253e11e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 03:48:11 +0000 Subject: [PATCH 790/2232] Sort buck genrule args the way buildifier wants https://github.com/bazelbuild/buildtools/tree/master/buildifier --- diff --git a/demo-rs/BUCK b/demo-rs/BUCK index d4164d3..a3f460b 100644 --- a/demo-rs/BUCK +++ b/demo-rs/BUCK @@ -20,17 +20,17 @@ cxx_library( genrule( name = "gen-header", srcs = ["src/main.rs"], + out = "generated.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", type = "cxxbridge", - out = "generated.h", ) genrule( name = "gen-source", srcs = ["src/main.rs"], + out = "generated.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", type = "cxxbridge", - out = "generated.cc", ) cxx_library( diff --git a/tests/BUCK b/tests/BUCK index 41781c8..4bfc9e8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -34,20 +34,20 @@ cxx_library( genrule( name = "gen-lib-header", srcs = ["ffi/lib.rs"], - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", out = "lib.rs.h", + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", ) genrule( name = "gen-lib-source", srcs = ["ffi/lib.rs"], - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", out = "lib.rs.cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) genrule( name = "gen-module-source", srcs = ["ffi/module.rs"], - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", out = "module.rs.cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) From 37531b477eb9576c798ce96d8672fdc3b8bd82d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 03:48:12 +0000 Subject: [PATCH 791/2232] Combine demo into one root directory --- diff --git a/Cargo.toml b/Cargo.toml index 5458398..c950d2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" documentation = "https://docs.rs/cxx" readme = "README.md" -exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] +exclude = ["/demo", "/gen", "/syntax", "/third-party"] keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] @@ -34,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo-rs", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] +members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK deleted file mode 100644 index f60200b..0000000 --- a/demo-cxx/BUCK +++ /dev/null @@ -1,17 +0,0 @@ -cxx_library( - name = "demo-cxx", - srcs = ["demo.cc"], - compiler_flags = ["-std=c++14"], - visibility = ["PUBLIC"], - deps = [ - ":include", - "//demo-rs:include", - ], -) - -cxx_library( - name = "include", - exported_headers = ["demo.h"], - visibility = ["PUBLIC"], - deps = ["//:core"], -) diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD deleted file mode 100644 index 7b1860a..0000000 --- a/demo-cxx/BUILD +++ /dev/null @@ -1,17 +0,0 @@ -cc_library( - name = "demo-cxx", - srcs = ["demo.cc"], - copts = ["-std=c++14"], - visibility = ["//visibility:public"], - deps = [ - ":include", - "//demo-rs:include", - ], -) - -cc_library( - name = "include", - hdrs = ["demo.h"], - visibility = ["//visibility:public"], - deps = ["//:core"], -) diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc deleted file mode 100644 index 21bdad4..0000000 --- a/demo-cxx/demo.cc +++ /dev/null @@ -1,21 +0,0 @@ -#include "demo-cxx/demo.h" -#include "demo-rs/src/main.rs.h" -#include - -namespace org { -namespace example { - -ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} - -ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } - -std::unique_ptr make_demo(rust::Str appname) { - return std::make_unique(std::string(appname)); -} - -const std::string &get_name(const ThingC &thing) { return thing.appname; } - -void do_thing(SharedThing state) { print_r(*state.y); } - -} // namespace example -} // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h deleted file mode 100644 index fafc474..0000000 --- a/demo-cxx/demo.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "rust/cxx.h" -#include -#include - -namespace org { -namespace example { - -class ThingC { -public: - ThingC(std::string appname); - ~ThingC(); - - std::string appname; -}; - -struct SharedThing; - -std::unique_ptr make_demo(rust::Str appname); -const std::string &get_name(const ThingC &thing); -void do_thing(SharedThing state); - -} // namespace example -} // namespace org diff --git a/demo-rs/BUCK b/demo-rs/BUCK deleted file mode 100644 index a3f460b..0000000 --- a/demo-rs/BUCK +++ /dev/null @@ -1,42 +0,0 @@ -rust_binary( - name = "demo-rs", - srcs = glob(["src/**"]), - deps = [ - ":gen", - "//:cxx", - "//demo-cxx:demo-cxx", - ], -) - -cxx_library( - name = "gen", - srcs = [":gen-source"], - deps = [ - ":include", - "//demo-cxx:include", - ], -) - -genrule( - name = "gen-header", - srcs = ["src/main.rs"], - out = "generated.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -genrule( - name = "gen-source", - srcs = ["src/main.rs"], - out = "generated.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -cxx_library( - name = "include", - exported_headers = { - "src/main.rs.h": ":gen-header", - }, - visibility = ["PUBLIC"], -) diff --git a/demo-rs/BUILD b/demo-rs/BUILD deleted file mode 100644 index e3ebb96..0000000 --- a/demo-rs/BUILD +++ /dev/null @@ -1,43 +0,0 @@ -load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") - -rust_binary( - name = "demo-rs", - srcs = glob(["src/**"]), - deps = [ - ":gen", - "//:cxx", - "//demo-cxx", - ], -) - -cc_library( - name = "gen", - srcs = [":gen-source"], - deps = [ - ":include", - "//demo-cxx:include", - ], -) - -genrule( - name = "gen-header", - srcs = ["src/main.rs"], - outs = ["main.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "gen-source", - srcs = ["src/main.rs"], - outs = ["generated.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "include", - hdrs = [":gen-header"], - include_prefix = "demo-rs/src", - visibility = ["//visibility:public"], -) diff --git a/demo-rs/Cargo.toml b/demo-rs/Cargo.toml deleted file mode 100644 index d2147ab..0000000 --- a/demo-rs/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "cxxbridge-demo" -version = "0.0.0" -authors = ["David Tolnay "] -edition = "2018" -publish = false - -[dependencies] -cxx = { path = ".." } - -[build-dependencies] -cxx-build = { path = "../gen/build" } diff --git a/demo-rs/build.rs b/demo-rs/build.rs deleted file mode 100644 index f32b8ef..0000000 --- a/demo-rs/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn main() { - cxx_build::bridge("src/main.rs") - .file("../demo-cxx/demo.cc") - .flag_if_supported("-std=c++14") - .compile("cxxbridge-demo"); - - println!("cargo:rerun-if-changed=src/main.rs"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); -} diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs deleted file mode 100644 index 66dfc79..0000000 --- a/demo-rs/src/main.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[cxx::bridge(namespace = org::example)] -mod ffi { - struct SharedThing { - z: i32, - y: Box, - x: UniquePtr, - } - - extern "C" { - include!("demo-cxx/demo.h"); - - type ThingC; - fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing); - } - - extern "Rust" { - type ThingR; - fn print_r(r: &ThingR); - } -} - -pub struct ThingR(usize); - -fn print_r(r: &ThingR) { - println!("called back with r={}", r.0); -} - -fn main() { - let x = ffi::make_demo("demo of cxx::bridge"); - println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - - ffi::do_thing(ffi::SharedThing { - z: 222, - y: Box::new(ThingR(333)), - x, - }); -} diff --git a/demo/BUCK b/demo/BUCK new file mode 100644 index 0000000..78e75e6 --- /dev/null +++ b/demo/BUCK @@ -0,0 +1,57 @@ +rust_binary( + name = "demo", + srcs = glob(["src/**/*.rs"]), + deps = [ + ":demo-sys", + ":gen", + "//:cxx", + ], +) + +cxx_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":demo-include", + ":include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + out = "generated.h", + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + out = "generated.cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", +) + +cxx_library( + name = "include", + exported_headers = { + "src/main.rs.h": ":gen-header", + }, +) + +cxx_library( + name = "demo-sys", + srcs = ["src/demo.cc"], + compiler_flags = ["-std=c++14"], + deps = [ + ":demo-include", + ":include", + ], +) + +cxx_library( + name = "demo-include", + exported_headers = ["include/demo.h"], + deps = ["//:core"], +) diff --git a/demo/BUILD b/demo/BUILD new file mode 100644 index 0000000..1edc65c --- /dev/null +++ b/demo/BUILD @@ -0,0 +1,58 @@ +load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") + +rust_binary( + name = "demo", + srcs = glob(["src/**/*.rs"]), + deps = [ + ":demo-sys", + ":gen", + "//:cxx", + ], +) + +cc_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":demo-include", + ":include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + outs = ["main.rs.h"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + outs = ["generated.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "demo/src", +) + +cc_library( + name = "demo-sys", + srcs = ["src/demo.cc"], + copts = ["-std=c++14"], + deps = [ + ":demo-include", + ":include", + ], +) + +cc_library( + name = "demo-include", + hdrs = ["include/demo.h"], + deps = ["//:core"], +) diff --git a/demo/Cargo.toml b/demo/Cargo.toml new file mode 100644 index 0000000..d2147ab --- /dev/null +++ b/demo/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cxxbridge-demo" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[dependencies] +cxx = { path = ".." } + +[build-dependencies] +cxx-build = { path = "../gen/build" } diff --git a/demo/build.rs b/demo/build.rs new file mode 100644 index 0000000..a3c31b3 --- /dev/null +++ b/demo/build.rs @@ -0,0 +1,10 @@ +fn main() { + cxx_build::bridge("src/main.rs") + .file("src/demo.cc") + .flag_if_supported("-std=c++14") + .compile("cxxbridge-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); +} diff --git a/demo/include/demo.h b/demo/include/demo.h new file mode 100644 index 0000000..fafc474 --- /dev/null +++ b/demo/include/demo.h @@ -0,0 +1,24 @@ +#pragma once +#include "rust/cxx.h" +#include +#include + +namespace org { +namespace example { + +class ThingC { +public: + ThingC(std::string appname); + ~ThingC(); + + std::string appname; +}; + +struct SharedThing; + +std::unique_ptr make_demo(rust::Str appname); +const std::string &get_name(const ThingC &thing); +void do_thing(SharedThing state); + +} // namespace example +} // namespace org diff --git a/demo/src/demo.cc b/demo/src/demo.cc new file mode 100644 index 0000000..79c693f --- /dev/null +++ b/demo/src/demo.cc @@ -0,0 +1,21 @@ +#include "demo/include/demo.h" +#include "demo/src/main.rs.h" +#include + +namespace org { +namespace example { + +ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} + +ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } + +std::unique_ptr make_demo(rust::Str appname) { + return std::make_unique(std::string(appname)); +} + +const std::string &get_name(const ThingC &thing) { return thing.appname; } + +void do_thing(SharedThing state) { print_r(*state.y); } + +} // namespace example +} // namespace org diff --git a/demo/src/main.rs b/demo/src/main.rs new file mode 100644 index 0000000..ee7e093 --- /dev/null +++ b/demo/src/main.rs @@ -0,0 +1,39 @@ +#[cxx::bridge(namespace = org::example)] +mod ffi { + struct SharedThing { + z: i32, + y: Box, + x: UniquePtr, + } + + extern "C" { + include!("demo/include/demo.h"); + + type ThingC; + fn make_demo(appname: &str) -> UniquePtr; + fn get_name(thing: &ThingC) -> &CxxString; + fn do_thing(state: SharedThing); + } + + extern "Rust" { + type ThingR; + fn print_r(r: &ThingR); + } +} + +pub struct ThingR(usize); + +fn print_r(r: &ThingR) { + println!("called back with r={}", r.0); +} + +fn main() { + let x = ffi::make_demo("demo of cxx::bridge"); + println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); + + ffi::do_thing(ffi::SharedThing { + z: 222, + y: Box::new(ThingR(333)), + x, + }); +} From 278f6fc8cdc30637b4998d3b840f1311f1cebf66 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 03:48:13 +0000 Subject: [PATCH 792/2232] Update demo documentation --- diff --git a/README.md b/README.md index 345ce7d..9c65df6 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,8 @@ function calls Rust's `len()`. ## Example -A runnable version of this example is provided under the *demo-rs* directory of -this repo (with the C++ side of the implementation in the *demo-cxx* directory). -To try it out, jump into demo-rs and run `cargo run`. +A runnable version of this example is provided under the *demo* directory of +this repo. To try it out, run `cargo run` from that directory. ```rust #[cxx::bridge] @@ -78,7 +77,7 @@ mod ffi { // One or more headers with the matching C++ declarations. Our code // generators don't read it but it gets #include'd and used in static // assertions to ensure our picture of the FFI boundary is accurate. - include!("demo-cxx/demo.h"); + include!("demo/include/demo.h"); // Zero or more opaque types which both languages can pass around but // only C++ can see the fields. @@ -107,10 +106,10 @@ get to call back and forth safely. Here are links to the complete set of source files involved in the demo: -- [demo-rs/src/main.rs](demo-rs/src/main.rs) -- [demo-rs/build.rs](demo-rs/build.rs) -- [demo-cxx/demo.h](demo-cxx/demo.h) -- [demo-cxx/demo.cc](demo-cxx/demo.cc) +- [demo/src/main.rs](demo/src/main.rs) +- [demo/build.rs](demo/build.rs) +- [demo/include/demo.h](demo/include/demo.h) +- [demo/src/demo.cc](demo/src/demo.cc) To look at the code generated in both languages for the example by the CXX code generators: @@ -118,10 +117,10 @@ generators: ```console # run Rust code generator and print to stdout # (requires https://github.com/dtolnay/cargo-expand) -$ cargo expand --manifest-path demo-rs/Cargo.toml +$ cargo expand --manifest-path demo/Cargo.toml # run C++ code generator and print to stdout -$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs +$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs ```
@@ -228,13 +227,13 @@ cxx-build = "0.3" fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build - .file("../demo-cxx/demo.cc") + .file("src/demo.cc") .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); } ``` diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 2560aee..768de15 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -15,18 +15,18 @@ //! //! fn main() { //! cxx_build::bridge("src/main.rs") -//! .file("../demo-cxx/demo.cc") +//! .file("src/demo.cc") //! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! println!("cargo:rerun-if-changed=src/demo.cc"); +//! println!("cargo:rerun-if-changed=include/demo.h"); //! } //! ``` //! -//! A runnable working setup with this build script is shown in the -//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. +//! A runnable working setup with this build script is shown in the *demo* +//! directory of [https://github.com/dtolnay/cxx]. //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx //! @@ -83,7 +83,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> Build { /// ```no_run /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) -/// .file("../demo-cxx/demo.cc") +/// .file("src/demo.cc") /// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` diff --git a/src/lib.rs b/src/lib.rs index a707338..94a9037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,10 +57,9 @@ //! //! # Example //! -//! A runnable version of this example is provided under the *demo-rs* directory -//! of [https://github.com/dtolnay/cxx] (with the C++ side of the implementation -//! in the *demo-cxx* directory). To try it out, jump into demo-rs and run -//! `cargo run`. +//! A runnable version of this example is provided under the *demo* directory of +//! [https://github.com/dtolnay/cxx]. To try it out, run `cargo run` from that +//! directory. //! //! ```no_run //! #[cxx::bridge] @@ -76,7 +75,7 @@ //! // One or more headers with the matching C++ declarations. Our code //! // generators don't read it but it gets #include'd and used in static //! // assertions to ensure our picture of the FFI boundary is accurate. -//! include!("demo-cxx/demo.h"); +//! include!("demo/include/demo.h"); //! //! // Zero or more opaque types which both languages can pass around but //! // only C++ can see the fields. @@ -113,10 +112,10 @@ //! //! Here are links to the complete set of source files involved in the demo: //! -//! - [demo-rs/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/src/main.rs) -//! - [demo-rs/build.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/build.rs) -//! - [demo-cxx/demo.h](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.h) -//! - [demo-cxx/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.cc) +//! - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs) +//! - [demo/build.rs](https://github.com/dtolnay/cxx/blob/master/demo/build.rs) +//! - [demo/include/demo.h](https://github.com/dtolnay/cxx/blob/master/demo/include/demo.h) +//! - [demo/src/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/demo.cc) //! //! To look at the code generated in both languages for the example by the CXX //! code generators: @@ -124,10 +123,10 @@ //! ```console //! # run Rust code generator and print to stdout //! # (requires https://github.com/dtolnay/cargo-expand) -//! $ cargo expand --manifest-path demo-rs/Cargo.toml +//! $ cargo expand --manifest-path demo/Cargo.toml //! //! # run C++ code generator and print to stdout -//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs +//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs //! ``` //! //!
@@ -237,13 +236,13 @@ //! //! fn main() { //! cxx_build::bridge("src/main.rs") // returns a cc::Build -//! .file("../demo-cxx/demo.cc") +//! .file("src/demo.cc") //! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! println!("cargo:rerun-if-changed=src/demo.cc"); +//! println!("cargo:rerun-if-changed=include/demo.h"); //! } //! ``` //! From f401e882a32adf3ed3c8fac4f1721f845d954b08 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 03:58:48 +0000 Subject: [PATCH 793/2232] Update demo path in CI workflow --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55431b6..315bac2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace --exclude cxx-test-suite msrv: @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@1.42.0 - - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo run --manifest-path demo/Cargo.toml buck: name: Buck @@ -66,7 +66,7 @@ jobs: cp third-party/Cargo.lock . cargo vendor --versioned-dirs --locked third-party/vendor - run: buck build :cxx#check --verbose=0 - - run: buck run demo-rs --verbose=0 + - run: buck run demo --verbose=0 - run: buck test ... --verbose=0 bazel: @@ -84,5 +84,5 @@ jobs: run: | cp third-party/Cargo.lock . cargo vendor --versioned-dirs --locked third-party/vendor - - run: bazel run demo-rs --verbose_failures --noshow_progress + - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress From 9ffb7b66e38be6ce9b7fa4c385012e55036adadb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:04:36 +0000 Subject: [PATCH 794/2232] Merge pull request #275 from dtolnay/demo Combine demo into one root directory --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55431b6..315bac2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace --exclude cxx-test-suite msrv: @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@1.42.0 - - run: cargo run --manifest-path demo-rs/Cargo.toml + - run: cargo run --manifest-path demo/Cargo.toml buck: name: Buck @@ -66,7 +66,7 @@ jobs: cp third-party/Cargo.lock . cargo vendor --versioned-dirs --locked third-party/vendor - run: buck build :cxx#check --verbose=0 - - run: buck run demo-rs --verbose=0 + - run: buck run demo --verbose=0 - run: buck test ... --verbose=0 bazel: @@ -84,5 +84,5 @@ jobs: run: | cp third-party/Cargo.lock . cargo vendor --versioned-dirs --locked third-party/vendor - - run: bazel run demo-rs --verbose_failures --noshow_progress + - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress diff --git a/Cargo.toml b/Cargo.toml index 5458398..c950d2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" documentation = "https://docs.rs/cxx" readme = "README.md" -exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"] +exclude = ["/demo", "/gen", "/syntax", "/third-party"] keywords = ["ffi"] categories = ["development-tools::ffi", "api-bindings"] @@ -34,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo-rs", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] +members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/README.md b/README.md index 345ce7d..9c65df6 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,8 @@ function calls Rust's `len()`. ## Example -A runnable version of this example is provided under the *demo-rs* directory of -this repo (with the C++ side of the implementation in the *demo-cxx* directory). -To try it out, jump into demo-rs and run `cargo run`. +A runnable version of this example is provided under the *demo* directory of +this repo. To try it out, run `cargo run` from that directory. ```rust #[cxx::bridge] @@ -78,7 +77,7 @@ mod ffi { // One or more headers with the matching C++ declarations. Our code // generators don't read it but it gets #include'd and used in static // assertions to ensure our picture of the FFI boundary is accurate. - include!("demo-cxx/demo.h"); + include!("demo/include/demo.h"); // Zero or more opaque types which both languages can pass around but // only C++ can see the fields. @@ -107,10 +106,10 @@ get to call back and forth safely. Here are links to the complete set of source files involved in the demo: -- [demo-rs/src/main.rs](demo-rs/src/main.rs) -- [demo-rs/build.rs](demo-rs/build.rs) -- [demo-cxx/demo.h](demo-cxx/demo.h) -- [demo-cxx/demo.cc](demo-cxx/demo.cc) +- [demo/src/main.rs](demo/src/main.rs) +- [demo/build.rs](demo/build.rs) +- [demo/include/demo.h](demo/include/demo.h) +- [demo/src/demo.cc](demo/src/demo.cc) To look at the code generated in both languages for the example by the CXX code generators: @@ -118,10 +117,10 @@ generators: ```console # run Rust code generator and print to stdout # (requires https://github.com/dtolnay/cargo-expand) -$ cargo expand --manifest-path demo-rs/Cargo.toml +$ cargo expand --manifest-path demo/Cargo.toml # run C++ code generator and print to stdout -$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs +$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs ```
@@ -228,13 +227,13 @@ cxx-build = "0.3" fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build - .file("../demo-cxx/demo.cc") + .file("src/demo.cc") .flag_if_supported("-std=c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/main.rs"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); } ``` diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK deleted file mode 100644 index f60200b..0000000 --- a/demo-cxx/BUCK +++ /dev/null @@ -1,17 +0,0 @@ -cxx_library( - name = "demo-cxx", - srcs = ["demo.cc"], - compiler_flags = ["-std=c++14"], - visibility = ["PUBLIC"], - deps = [ - ":include", - "//demo-rs:include", - ], -) - -cxx_library( - name = "include", - exported_headers = ["demo.h"], - visibility = ["PUBLIC"], - deps = ["//:core"], -) diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD deleted file mode 100644 index 7b1860a..0000000 --- a/demo-cxx/BUILD +++ /dev/null @@ -1,17 +0,0 @@ -cc_library( - name = "demo-cxx", - srcs = ["demo.cc"], - copts = ["-std=c++14"], - visibility = ["//visibility:public"], - deps = [ - ":include", - "//demo-rs:include", - ], -) - -cc_library( - name = "include", - hdrs = ["demo.h"], - visibility = ["//visibility:public"], - deps = ["//:core"], -) diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc deleted file mode 100644 index 21bdad4..0000000 --- a/demo-cxx/demo.cc +++ /dev/null @@ -1,21 +0,0 @@ -#include "demo-cxx/demo.h" -#include "demo-rs/src/main.rs.h" -#include - -namespace org { -namespace example { - -ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} - -ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } - -std::unique_ptr make_demo(rust::Str appname) { - return std::make_unique(std::string(appname)); -} - -const std::string &get_name(const ThingC &thing) { return thing.appname; } - -void do_thing(SharedThing state) { print_r(*state.y); } - -} // namespace example -} // namespace org diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h deleted file mode 100644 index fafc474..0000000 --- a/demo-cxx/demo.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "rust/cxx.h" -#include -#include - -namespace org { -namespace example { - -class ThingC { -public: - ThingC(std::string appname); - ~ThingC(); - - std::string appname; -}; - -struct SharedThing; - -std::unique_ptr make_demo(rust::Str appname); -const std::string &get_name(const ThingC &thing); -void do_thing(SharedThing state); - -} // namespace example -} // namespace org diff --git a/demo-rs/BUCK b/demo-rs/BUCK deleted file mode 100644 index a3f460b..0000000 --- a/demo-rs/BUCK +++ /dev/null @@ -1,42 +0,0 @@ -rust_binary( - name = "demo-rs", - srcs = glob(["src/**"]), - deps = [ - ":gen", - "//:cxx", - "//demo-cxx:demo-cxx", - ], -) - -cxx_library( - name = "gen", - srcs = [":gen-source"], - deps = [ - ":include", - "//demo-cxx:include", - ], -) - -genrule( - name = "gen-header", - srcs = ["src/main.rs"], - out = "generated.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -genrule( - name = "gen-source", - srcs = ["src/main.rs"], - out = "generated.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -cxx_library( - name = "include", - exported_headers = { - "src/main.rs.h": ":gen-header", - }, - visibility = ["PUBLIC"], -) diff --git a/demo-rs/BUILD b/demo-rs/BUILD deleted file mode 100644 index e3ebb96..0000000 --- a/demo-rs/BUILD +++ /dev/null @@ -1,43 +0,0 @@ -load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") - -rust_binary( - name = "demo-rs", - srcs = glob(["src/**"]), - deps = [ - ":gen", - "//:cxx", - "//demo-cxx", - ], -) - -cc_library( - name = "gen", - srcs = [":gen-source"], - deps = [ - ":include", - "//demo-cxx:include", - ], -) - -genrule( - name = "gen-header", - srcs = ["src/main.rs"], - outs = ["main.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "gen-source", - srcs = ["src/main.rs"], - outs = ["generated.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "include", - hdrs = [":gen-header"], - include_prefix = "demo-rs/src", - visibility = ["//visibility:public"], -) diff --git a/demo-rs/Cargo.toml b/demo-rs/Cargo.toml deleted file mode 100644 index d2147ab..0000000 --- a/demo-rs/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "cxxbridge-demo" -version = "0.0.0" -authors = ["David Tolnay "] -edition = "2018" -publish = false - -[dependencies] -cxx = { path = ".." } - -[build-dependencies] -cxx-build = { path = "../gen/build" } diff --git a/demo-rs/build.rs b/demo-rs/build.rs deleted file mode 100644 index f32b8ef..0000000 --- a/demo-rs/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn main() { - cxx_build::bridge("src/main.rs") - .file("../demo-cxx/demo.cc") - .flag_if_supported("-std=c++14") - .compile("cxxbridge-demo"); - - println!("cargo:rerun-if-changed=src/main.rs"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); - println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); -} diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs deleted file mode 100644 index 66dfc79..0000000 --- a/demo-rs/src/main.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[cxx::bridge(namespace = org::example)] -mod ffi { - struct SharedThing { - z: i32, - y: Box, - x: UniquePtr, - } - - extern "C" { - include!("demo-cxx/demo.h"); - - type ThingC; - fn make_demo(appname: &str) -> UniquePtr; - fn get_name(thing: &ThingC) -> &CxxString; - fn do_thing(state: SharedThing); - } - - extern "Rust" { - type ThingR; - fn print_r(r: &ThingR); - } -} - -pub struct ThingR(usize); - -fn print_r(r: &ThingR) { - println!("called back with r={}", r.0); -} - -fn main() { - let x = ffi::make_demo("demo of cxx::bridge"); - println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); - - ffi::do_thing(ffi::SharedThing { - z: 222, - y: Box::new(ThingR(333)), - x, - }); -} diff --git a/demo/BUCK b/demo/BUCK new file mode 100644 index 0000000..78e75e6 --- /dev/null +++ b/demo/BUCK @@ -0,0 +1,57 @@ +rust_binary( + name = "demo", + srcs = glob(["src/**/*.rs"]), + deps = [ + ":demo-sys", + ":gen", + "//:cxx", + ], +) + +cxx_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":demo-include", + ":include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + out = "generated.h", + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + out = "generated.cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", +) + +cxx_library( + name = "include", + exported_headers = { + "src/main.rs.h": ":gen-header", + }, +) + +cxx_library( + name = "demo-sys", + srcs = ["src/demo.cc"], + compiler_flags = ["-std=c++14"], + deps = [ + ":demo-include", + ":include", + ], +) + +cxx_library( + name = "demo-include", + exported_headers = ["include/demo.h"], + deps = ["//:core"], +) diff --git a/demo/BUILD b/demo/BUILD new file mode 100644 index 0000000..1edc65c --- /dev/null +++ b/demo/BUILD @@ -0,0 +1,58 @@ +load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") + +rust_binary( + name = "demo", + srcs = glob(["src/**/*.rs"]), + deps = [ + ":demo-sys", + ":gen", + "//:cxx", + ], +) + +cc_library( + name = "gen", + srcs = [":gen-source"], + deps = [ + ":demo-include", + ":include", + ], +) + +genrule( + name = "gen-header", + srcs = ["src/main.rs"], + outs = ["main.rs.h"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], +) + +genrule( + name = "gen-source", + srcs = ["src/main.rs"], + outs = ["generated.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) + +cc_library( + name = "include", + hdrs = [":gen-header"], + include_prefix = "demo/src", +) + +cc_library( + name = "demo-sys", + srcs = ["src/demo.cc"], + copts = ["-std=c++14"], + deps = [ + ":demo-include", + ":include", + ], +) + +cc_library( + name = "demo-include", + hdrs = ["include/demo.h"], + deps = ["//:core"], +) diff --git a/demo/Cargo.toml b/demo/Cargo.toml new file mode 100644 index 0000000..d2147ab --- /dev/null +++ b/demo/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cxxbridge-demo" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[dependencies] +cxx = { path = ".." } + +[build-dependencies] +cxx-build = { path = "../gen/build" } diff --git a/demo/build.rs b/demo/build.rs new file mode 100644 index 0000000..a3c31b3 --- /dev/null +++ b/demo/build.rs @@ -0,0 +1,10 @@ +fn main() { + cxx_build::bridge("src/main.rs") + .file("src/demo.cc") + .flag_if_supported("-std=c++14") + .compile("cxxbridge-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); +} diff --git a/demo/include/demo.h b/demo/include/demo.h new file mode 100644 index 0000000..fafc474 --- /dev/null +++ b/demo/include/demo.h @@ -0,0 +1,24 @@ +#pragma once +#include "rust/cxx.h" +#include +#include + +namespace org { +namespace example { + +class ThingC { +public: + ThingC(std::string appname); + ~ThingC(); + + std::string appname; +}; + +struct SharedThing; + +std::unique_ptr make_demo(rust::Str appname); +const std::string &get_name(const ThingC &thing); +void do_thing(SharedThing state); + +} // namespace example +} // namespace org diff --git a/demo/src/demo.cc b/demo/src/demo.cc new file mode 100644 index 0000000..79c693f --- /dev/null +++ b/demo/src/demo.cc @@ -0,0 +1,21 @@ +#include "demo/include/demo.h" +#include "demo/src/main.rs.h" +#include + +namespace org { +namespace example { + +ThingC::ThingC(std::string appname) : appname(std::move(appname)) {} + +ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; } + +std::unique_ptr make_demo(rust::Str appname) { + return std::make_unique(std::string(appname)); +} + +const std::string &get_name(const ThingC &thing) { return thing.appname; } + +void do_thing(SharedThing state) { print_r(*state.y); } + +} // namespace example +} // namespace org diff --git a/demo/src/main.rs b/demo/src/main.rs new file mode 100644 index 0000000..ee7e093 --- /dev/null +++ b/demo/src/main.rs @@ -0,0 +1,39 @@ +#[cxx::bridge(namespace = org::example)] +mod ffi { + struct SharedThing { + z: i32, + y: Box, + x: UniquePtr, + } + + extern "C" { + include!("demo/include/demo.h"); + + type ThingC; + fn make_demo(appname: &str) -> UniquePtr; + fn get_name(thing: &ThingC) -> &CxxString; + fn do_thing(state: SharedThing); + } + + extern "Rust" { + type ThingR; + fn print_r(r: &ThingR); + } +} + +pub struct ThingR(usize); + +fn print_r(r: &ThingR) { + println!("called back with r={}", r.0); +} + +fn main() { + let x = ffi::make_demo("demo of cxx::bridge"); + println!("this is a {}", ffi::get_name(x.as_ref().unwrap())); + + ffi::do_thing(ffi::SharedThing { + z: 222, + y: Box::new(ThingR(333)), + x, + }); +} diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 2560aee..768de15 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -15,18 +15,18 @@ //! //! fn main() { //! cxx_build::bridge("src/main.rs") -//! .file("../demo-cxx/demo.cc") +//! .file("src/demo.cc") //! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! println!("cargo:rerun-if-changed=src/demo.cc"); +//! println!("cargo:rerun-if-changed=include/demo.h"); //! } //! ``` //! -//! A runnable working setup with this build script is shown in the -//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx]. +//! A runnable working setup with this build script is shown in the *demo* +//! directory of [https://github.com/dtolnay/cxx]. //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx //! @@ -83,7 +83,7 @@ pub fn bridge(rust_source_file: impl AsRef) -> Build { /// ```no_run /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) -/// .file("../demo-cxx/demo.cc") +/// .file("src/demo.cc") /// .flag_if_supported("-std=c++11") /// .compile("cxxbridge-demo"); /// ``` diff --git a/src/lib.rs b/src/lib.rs index a707338..94a9037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,10 +57,9 @@ //! //! # Example //! -//! A runnable version of this example is provided under the *demo-rs* directory -//! of [https://github.com/dtolnay/cxx] (with the C++ side of the implementation -//! in the *demo-cxx* directory). To try it out, jump into demo-rs and run -//! `cargo run`. +//! A runnable version of this example is provided under the *demo* directory of +//! [https://github.com/dtolnay/cxx]. To try it out, run `cargo run` from that +//! directory. //! //! ```no_run //! #[cxx::bridge] @@ -76,7 +75,7 @@ //! // One or more headers with the matching C++ declarations. Our code //! // generators don't read it but it gets #include'd and used in static //! // assertions to ensure our picture of the FFI boundary is accurate. -//! include!("demo-cxx/demo.h"); +//! include!("demo/include/demo.h"); //! //! // Zero or more opaque types which both languages can pass around but //! // only C++ can see the fields. @@ -113,10 +112,10 @@ //! //! Here are links to the complete set of source files involved in the demo: //! -//! - [demo-rs/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/src/main.rs) -//! - [demo-rs/build.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/build.rs) -//! - [demo-cxx/demo.h](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.h) -//! - [demo-cxx/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.cc) +//! - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs) +//! - [demo/build.rs](https://github.com/dtolnay/cxx/blob/master/demo/build.rs) +//! - [demo/include/demo.h](https://github.com/dtolnay/cxx/blob/master/demo/include/demo.h) +//! - [demo/src/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/demo.cc) //! //! To look at the code generated in both languages for the example by the CXX //! code generators: @@ -124,10 +123,10 @@ //! ```console //! # run Rust code generator and print to stdout //! # (requires https://github.com/dtolnay/cargo-expand) -//! $ cargo expand --manifest-path demo-rs/Cargo.toml +//! $ cargo expand --manifest-path demo/Cargo.toml //! //! # run C++ code generator and print to stdout -//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs +//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs //! ``` //! //!
@@ -237,13 +236,13 @@ //! //! fn main() { //! cxx_build::bridge("src/main.rs") // returns a cc::Build -//! .file("../demo-cxx/demo.cc") +//! .file("src/demo.cc") //! .flag_if_supported("-std=c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/main.rs"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h"); -//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc"); +//! println!("cargo:rerun-if-changed=src/demo.cc"); +//! println!("cargo:rerun-if-changed=include/demo.h"); //! } //! ``` //! From bcc0a1c9f943401d58e9ee9111958ab242279cfc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:09:08 +0000 Subject: [PATCH 795/2232] Release 0.3.9 --- diff --git a/Cargo.toml b/Cargo.toml index c950d2e..5e6fe27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.8" # remember to update html_root_url +version = "0.3.9" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge03" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.3.8", path = "macro" } +cxxbridge-macro = { version = "=0.3.9", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.3.8", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.3.9", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.3.8", path = "gen/build" } +cxx-build = { version = "=0.3.9", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 061237a..5bff819 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.3.8" +version = "0.3.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 2e536f7..eed5281 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.8" +version = "0.3.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index a49e22f..68ec3ec 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.8" +version = "0.3.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index aabd4d1..b273229 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.8" +version = "0.3.9" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 94a9037..bbd79f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.8")] +#![doc(html_root_url = "https://docs.rs/cxx/0.3.9")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 268c9f4..1f3e760 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.8" +version = "0.3.9" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.8" +version = "0.3.9" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.8" +version = "0.3.9" dependencies = [ "clap", "codespan-reporting", @@ -124,11 +124,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.3.8" +version = "0.3.9" [[package]] name = "cxxbridge-macro" -version = "0.3.8" +version = "0.3.9" dependencies = [ "cxx", "proc-macro2", From 97b69c66753f66ab671eb785c32ee6599a4ee53a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:13:21 +0000 Subject: [PATCH 796/2232] Ignore or_fun_call lint --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 768de15..5c0f12a 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -49,6 +49,7 @@ clippy::inherent_to_string, clippy::needless_doctest_main, clippy::new_without_default, + clippy::or_fun_call, clippy::toplevel_ref_arg )] diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index c5cc396..14b75e6 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -3,6 +3,7 @@ clippy::inherent_to_string, clippy::large_enum_variant, clippy::new_without_default, + clippy::or_fun_call, clippy::toplevel_ref_arg )] From b6614db28b1c81ec2e9c980ec541720c3d53cad7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:17:48 +0000 Subject: [PATCH 797/2232] Add symlink_dir to fs lib To be used in cxx-build refactor. --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 82cb5cc..c554ef0 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -105,7 +105,7 @@ fn canonicalize(path: impl AsRef) -> Result { } #[cfg(unix)] -use self::fs::symlink as symlink_or_copy; +use self::fs::symlink_file as symlink_or_copy; #[cfg(windows)] fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { diff --git a/gen/src/fs.rs b/gen/src/fs.rs index d1b0b70..77488f8 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -82,11 +82,12 @@ pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { } } -#[cfg(unix)] -pub(crate) fn symlink(src: impl AsRef, dst: impl AsRef) -> Result<()> { - let src = src.as_ref(); - let dst = dst.as_ref(); - match std::os::unix::fs::symlink(src, dst) { +fn symlink<'a>( + src: &'a Path, + dst: &'a Path, + fun: fn(&'a Path, &'a Path) -> io::Result<()>, +) -> Result<()> { + match fun(src, dst) { Ok(()) => Ok(()), Err(e) => err!( e, @@ -97,19 +98,31 @@ pub(crate) fn symlink(src: impl AsRef, dst: impl AsRef) -> Result<() } } +#[cfg(unix)] +#[allow(unused_imports)] +pub(crate) use self::symlink_file as symlink_dir; + +#[cfg(unix)] +pub(crate) fn symlink_file(src: impl AsRef, dst: impl AsRef) -> Result<()> { + symlink(src.as_ref(), dst.as_ref(), std::os::unix::fs::symlink) +} + #[cfg(windows)] pub(crate) fn symlink_file(src: impl AsRef, dst: impl AsRef) -> Result<()> { - let src = src.as_ref(); - let dst = dst.as_ref(); - match std::os::windows::fs::symlink_file(src, dst) { - Ok(()) => Ok(()), - Err(e) => err!( - e, - "Failed to create symlink `{}` pointing to `{}`", - dst, - src, - ), - } + symlink( + src.as_ref(), + dst.as_ref(), + std::os::windows::fs::symlink_file, + ) +} + +#[cfg(windows)] +pub(crate) fn symlink_dir(src: impl AsRef, dst: impl AsRef) -> Result<()> { + symlink( + src.as_ref(), + dst.as_ref(), + std::os::windows::fs::symlink_dir, + ) } pub(crate) fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { From 7ac98fa9726adfb051fda2822c04ebc7e4892d16 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:16 +0000 Subject: [PATCH 798/2232] Introduce newtype for target dir I plan to cache the target dir determination, which will require passing it around somewhat more in function arguments. Having a unique type will make that less error prone. --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 91f229d..3bddc6e 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -1,9 +1,10 @@ use crate::error::TargetDirError; +use crate::paths::TargetDir; use std::path::PathBuf; use std::process::Command; use std::str; -pub(crate) fn target_dir() -> Result { +pub(crate) fn target_dir() -> Result { let cargo = option_env!("CARGO").unwrap_or("cargo"); let output = Command::new(cargo) .arg("metadata") @@ -24,7 +25,7 @@ pub(crate) fn target_dir() -> Result { let close_quote_index = metadata.find('"')?; let string = &metadata[..close_quote_index]; let target_directory = string.replace("\\\\", "\\"); - Some(PathBuf::from(target_directory)) + Some(TargetDir(PathBuf::from(target_directory))) })() .ok_or(TargetDirError::NotFound) } diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index c554ef0..e6b29bd 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -2,8 +2,18 @@ use crate::cargo; use crate::error::{Error, Result}; use crate::gen::fs; use std::env; +use std::ops::Deref; use std::path::{Path, PathBuf}; +pub(crate) struct TargetDir(pub PathBuf); + +impl Deref for TargetDir { + type Target = Path; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + fn out_dir() -> Result { env::var_os("OUT_DIR") .map(PathBuf::from) @@ -72,7 +82,7 @@ pub(crate) fn include_dir() -> Result { Ok(target_dir.join("cxxbridge")) } -fn target_dir() -> Result { +fn target_dir() -> Result { let fallback_err = match cargo::target_dir() { Ok(target_dir) => return Ok(target_dir), Err(err) => Error::TargetDir(err), @@ -82,7 +92,7 @@ fn target_dir() -> Result { let mut dir = out_dir().and_then(canonicalize)?; loop { if dir.ends_with("target") { - return Ok(dir); + return Ok(TargetDir(dir)); } if !dir.pop() { return Err(fallback_err); From cad9ff6c9e98431658d0797ae29c4ef004584357 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 799/2232] Cache computation of target directory Previously we would be repeating at least 7 successive Cargo invocation. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 5c0f12a..eea9e68 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -62,6 +62,7 @@ mod syntax; use crate::error::Result; use crate::gen::error::report; use crate::gen::{fs, Opt}; +use crate::paths::TargetDir; use cc::Build; use std::io::{self, Write}; use std::iter; @@ -98,34 +99,39 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let mut build = paths::cc_build(); + let ref target_dir = paths::target_dir()?; + let mut build = paths::cc_build(target_dir); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - write_header()?; + write_header(target_dir)?; for path in rust_source_files { - generate_bridge(&mut build, path.as_ref())?; + generate_bridge(&mut build, path.as_ref(), target_dir)?; } Ok(build) } -fn write_header() -> Result<()> { - let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h"); +fn write_header(target_dir: &TargetDir) -> Result<()> { + let ref cxx_h = paths::include_dir(target_dir)?.join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); Ok(()) } -fn generate_bridge(build: &mut Build, rust_source_file: &Path) -> Result<()> { +fn generate_bridge( + build: &mut Build, + rust_source_file: &Path, + target_dir: &TargetDir, +) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); - let header_path = paths::out_with_extension(rust_source_file, ".h")?; + let header_path = paths::out_with_extension(rust_source_file, ".h", target_dir)?; fs::create_dir_all(header_path.parent().unwrap())?; write(&header_path, &generated.header)?; - paths::symlink_header(&header_path, rust_source_file); + paths::symlink_header(&header_path, rust_source_file, target_dir); - let implementation_path = paths::out_with_extension(rust_source_file, ".cc")?; + let implementation_path = paths::out_with_extension(rust_source_file, ".cc", target_dir)?; write(&implementation_path, &generated.implementation)?; build.file(&implementation_path); Ok(()) diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index e6b29bd..3f4814e 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -20,26 +20,26 @@ fn out_dir() -> Result { .ok_or(Error::MissingOutDir) } -pub(crate) fn cc_build() -> cc::Build { - try_cc_build().unwrap_or_default() +pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { + try_cc_build(target_dir).unwrap_or_default() } -fn try_cc_build() -> Result { +fn try_cc_build(target_dir: &TargetDir) -> Result { let mut build = cc::Build::new(); - build.include(include_dir()?); - build.include(target_dir()?.parent().unwrap()); + build.include(include_dir(target_dir)?); + build.include(target_dir.parent().unwrap()); Ok(build) } // Symlink the header file into a predictable place. The header generated from // path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(path: &Path, original: &Path) { - let _ = try_symlink_header(path, original); +pub(crate) fn symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) { + let _ = try_symlink_header(path, original, target_dir); } -fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(original)?; - let ref dst = include_dir()?.join(suffix); +fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> Result<()> { + let suffix = relative_to_parent_of_target_dir(original, target_dir)?; + let ref dst = include_dir(target_dir)?.join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); @@ -53,8 +53,7 @@ fn try_symlink_header(path: &Path, original: &Path) -> Result<()> { Ok(()) } -fn relative_to_parent_of_target_dir(original: &Path) -> Result { - let target_dir = target_dir()?; +fn relative_to_parent_of_target_dir(original: &Path, target_dir: &TargetDir) -> Result { let mut outer = target_dir.parent().unwrap(); let original = canonicalize(original)?; loop { @@ -68,21 +67,24 @@ fn relative_to_parent_of_target_dir(original: &Path) -> Result { } } -pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result { +pub(crate) fn out_with_extension( + path: &Path, + ext: &str, + target_dir: &TargetDir, +) -> Result { let mut file_name = path.file_name().unwrap().to_owned(); file_name.push(ext); let out_dir = out_dir()?; - let rel = relative_to_parent_of_target_dir(path)?; + let rel = relative_to_parent_of_target_dir(path, target_dir)?; Ok(out_dir.join(rel).with_file_name(file_name)) } -pub(crate) fn include_dir() -> Result { - let target_dir = target_dir()?; +pub(crate) fn include_dir(target_dir: &TargetDir) -> Result { Ok(target_dir.join("cxxbridge")) } -fn target_dir() -> Result { +pub(crate) fn target_dir() -> Result { let fallback_err = match cargo::target_dir() { Ok(target_dir) => return Ok(target_dir), Err(err) => Error::TargetDir(err), From 4057e8ea212a7858ab11756ea6d51d70d2bf3878 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 800/2232] Include dir can be infallible Enabled by the target dir refactor. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index eea9e68..15a4348 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -113,7 +113,7 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul } fn write_header(target_dir: &TargetDir) -> Result<()> { - let ref cxx_h = paths::include_dir(target_dir)?.join("rust").join("cxx.h"); + let ref cxx_h = paths::include_dir(target_dir).join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); Ok(()) } diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 3f4814e..6bc3e8a 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -26,7 +26,7 @@ pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { fn try_cc_build(target_dir: &TargetDir) -> Result { let mut build = cc::Build::new(); - build.include(include_dir(target_dir)?); + build.include(include_dir(target_dir)); build.include(target_dir.parent().unwrap()); Ok(build) } @@ -39,7 +39,7 @@ pub(crate) fn symlink_header(path: &Path, original: &Path, target_dir: &TargetDi fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> Result<()> { let suffix = relative_to_parent_of_target_dir(original, target_dir)?; - let ref dst = include_dir(target_dir)?.join(suffix); + let ref dst = include_dir(target_dir).join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); @@ -80,8 +80,8 @@ pub(crate) fn out_with_extension( Ok(out_dir.join(rel).with_file_name(file_name)) } -pub(crate) fn include_dir(target_dir: &TargetDir) -> Result { - Ok(target_dir.join("cxxbridge")) +pub(crate) fn include_dir(target_dir: &TargetDir) -> PathBuf { + target_dir.join("cxxbridge") } pub(crate) fn target_dir() -> Result { From 28241a0d362ad9ab326f52e760428ae39f4e453b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 801/2232] Move cargo based logic out of paths module --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 22f53f6..2f94426 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,7 +59,7 @@ mod gen; mod paths; mod syntax; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::{fs, Opt}; use crate::paths::TargetDir; @@ -99,7 +99,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let ref target_dir = paths::target_dir()?; + let ref target_dir = target_dir()?; let mut build = paths::cc_build(target_dir); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate @@ -151,3 +151,16 @@ fn write(path: &Path, content: &[u8]) -> Result<()> { fs::write(path, content)?; Ok(()) } + +fn target_dir() -> Result { + let fallback_err = match cargo::target_dir() { + Ok(target_dir) => return Ok(target_dir), + Err(err) => Error::TargetDir(err), + }; + + // Fallback if Cargo did not work. + match paths::search_parents_for_target_dir() { + Some(target_dir) => Ok(target_dir), + None => Err(fallback_err), + } +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 870c49d..47a28a0 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,4 +1,3 @@ -use crate::cargo; use crate::error::{Error, Result}; use crate::gen::fs; use std::env; @@ -80,20 +79,14 @@ pub(crate) fn include_dir(target_dir: &TargetDir) -> PathBuf { target_dir.join("cxxbridge") } -pub(crate) fn target_dir() -> Result { - let fallback_err = match cargo::target_dir() { - Ok(target_dir) => return Ok(target_dir), - Err(err) => Error::TargetDir(err), - }; - - // Fallback if Cargo did not work. - let mut dir = out_dir().and_then(canonicalize)?; +pub(crate) fn search_parents_for_target_dir() -> Option { + let mut dir = out_dir().and_then(canonicalize).ok()?; loop { if dir.ends_with("target") { - return Ok(TargetDir(dir)); + return Some(TargetDir(dir)); } if !dir.pop() { - return Err(fallback_err); + return None; } } } From 58c5b150d329f94f3919075d588149715fd416ab Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 802/2232] Construction of cc build can be infallible Enabled by the target dir refactor. --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 6bc3e8a..870c49d 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -21,14 +21,10 @@ fn out_dir() -> Result { } pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { - try_cc_build(target_dir).unwrap_or_default() -} - -fn try_cc_build(target_dir: &TargetDir) -> Result { let mut build = cc::Build::new(); build.include(include_dir(target_dir)); build.include(target_dir.parent().unwrap()); - Ok(build) + build } // Symlink the header file into a predictable place. The header generated from From 073dbf58ea2a8d019ae075cfa41e87d9de047630 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 803/2232] Continue if target dir not found --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 3bddc6e..6a4f1a6 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -1,19 +1,18 @@ -use crate::error::TargetDirError; use crate::paths::TargetDir; use std::path::PathBuf; use std::process::Command; use std::str; -pub(crate) fn target_dir() -> Result { - let cargo = option_env!("CARGO").unwrap_or("cargo"); - let output = Command::new(cargo) - .arg("metadata") - .arg("--no-deps") - .arg("--format-version=1") - .output() - .map_err(TargetDirError::Io)?; - +pub(crate) fn target_dir() -> TargetDir { (|| { + let cargo = option_env!("CARGO").unwrap_or("cargo"); + let output = Command::new(cargo) + .arg("metadata") + .arg("--no-deps") + .arg("--format-version=1") + .output() + .ok()?; + // Cargo only outputs utf8 encoded JSON. let mut metadata = str::from_utf8(&output.stdout).ok()?; @@ -25,7 +24,7 @@ pub(crate) fn target_dir() -> Result { let close_quote_index = metadata.find('"')?; let string = &metadata[..close_quote_index]; let target_directory = string.replace("\\\\", "\\"); - Some(TargetDir(PathBuf::from(target_directory))) + Some(TargetDir::Path(PathBuf::from(target_directory))) })() - .ok_or(TargetDirError::NotFound) + .unwrap_or(TargetDir::Unknown) } diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index ed4c6af..0ed4d74 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,28 +1,19 @@ use crate::gen::fs; use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { MissingOutDir, - TargetDir(TargetDirError), Fs(fs::Error), } -#[derive(Debug)] -pub(crate) enum TargetDirError { - Io(io::Error), - NotFound, -} - impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), - Error::TargetDir(_) => write!(f, "unable to identify target dir"), Error::Fs(err) => err.fmt(f), } } @@ -31,10 +22,6 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::TargetDir(err) => match err { - TargetDirError::Io(err) => Some(err), - TargetDirError::NotFound => None, - }, Error::Fs(err) => err.source(), _ => None, } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 2f94426..111f592 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,7 +59,7 @@ mod gen; mod paths; mod syntax; -use crate::error::{Error, Result}; +use crate::error::Result; use crate::gen::error::report; use crate::gen::{fs, Opt}; use crate::paths::TargetDir; @@ -99,7 +99,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let ref target_dir = target_dir()?; + let ref target_dir = target_dir(); let mut build = paths::cc_build(target_dir); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate @@ -152,15 +152,10 @@ fn write(path: &Path, content: &[u8]) -> Result<()> { Ok(()) } -fn target_dir() -> Result { - let fallback_err = match cargo::target_dir() { - Ok(target_dir) => return Ok(target_dir), - Err(err) => Error::TargetDir(err), - }; - - // Fallback if Cargo did not work. - match paths::search_parents_for_target_dir() { - Some(target_dir) => Ok(target_dir), - None => Err(fallback_err), +fn target_dir() -> TargetDir { + match cargo::target_dir() { + target_dir @ TargetDir::Path(_) => target_dir, + // Fallback if Cargo did not work. + TargetDir::Unknown => paths::search_parents_for_target_dir(), } } diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 47a28a0..1814f82 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,16 +1,11 @@ use crate::error::{Error, Result}; use crate::gen::fs; use std::env; -use std::ops::Deref; use std::path::{Path, PathBuf}; -pub(crate) struct TargetDir(pub PathBuf); - -impl Deref for TargetDir { - type Target = Path; - fn deref(&self) -> &Self::Target { - &self.0 - } +pub(crate) enum TargetDir { + Path(PathBuf), + Unknown, } fn out_dir() -> Result { @@ -22,13 +17,18 @@ fn out_dir() -> Result { pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { let mut build = cc::Build::new(); build.include(include_dir(target_dir)); - build.include(target_dir.parent().unwrap()); + if let TargetDir::Path(target_dir) = target_dir { + build.include(target_dir.parent().unwrap()); + } build } // Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. +// path/to/mod.rs gets linked to target/cxxbridge/path/to/mod.rs.h. pub(crate) fn symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) { + if let TargetDir::Unknown = target_dir { + return; + } let _ = try_symlink_header(path, original, target_dir); } @@ -49,7 +49,10 @@ fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> R } fn relative_to_parent_of_target_dir(original: &Path, target_dir: &TargetDir) -> Result { - let mut outer = target_dir.parent().unwrap(); + let mut outer = match target_dir { + TargetDir::Path(target_dir) => target_dir.parent().unwrap(), + TargetDir::Unknown => unimplemented!(), // FIXME + }; let original = canonicalize(original)?; loop { if let Ok(suffix) = original.strip_prefix(outer) { @@ -76,17 +79,23 @@ pub(crate) fn out_with_extension( } pub(crate) fn include_dir(target_dir: &TargetDir) -> PathBuf { - target_dir.join("cxxbridge") + match target_dir { + TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), + TargetDir::Unknown => out_dir().unwrap().join("cxxbridge"), // FIXME no unwrap + } } -pub(crate) fn search_parents_for_target_dir() -> Option { - let mut dir = out_dir().and_then(canonicalize).ok()?; +pub(crate) fn search_parents_for_target_dir() -> TargetDir { + let mut dir = match out_dir().and_then(canonicalize) { + Ok(dir) => dir, + Err(_) => return TargetDir::Unknown, + }; loop { if dir.ends_with("target") { - return Some(TargetDir(dir)); + return TargetDir::Path(dir); } if !dir.pop() { - return None; + return TargetDir::Unknown; } } } From 7f4b3ca2f9b84fea9358b6187ae1dfe096a8b9f3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:18:17 +0000 Subject: [PATCH 804/2232] Write header can be infallible Enabled by the target dir refactor. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 15a4348..22f53f6 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -103,7 +103,7 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul let mut build = paths::cc_build(target_dir); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - write_header(target_dir)?; + write_header(target_dir); for path in rust_source_files { generate_bridge(&mut build, path.as_ref(), target_dir)?; @@ -112,10 +112,9 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul Ok(build) } -fn write_header(target_dir: &TargetDir) -> Result<()> { +fn write_header(target_dir: &TargetDir) { let ref cxx_h = paths::include_dir(target_dir).join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); - Ok(()) } fn generate_bridge( From 8c0849a4f268b3f2fc6fd0d0d4ebf830583bbfd7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:38 +0000 Subject: [PATCH 805/2232] Keep target dir in Project struct We'll put the OUT_DIR in here as well, to reduce the amount of code that needs to be fallible. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 111f592..02df064 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -98,39 +98,51 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> }) } +struct Project { + target_dir: TargetDir, +} + +impl Project { + fn init() -> Self { + Project { + target_dir: match cargo::target_dir() { + target_dir @ TargetDir::Path(_) => target_dir, + // Fallback if Cargo did not work. + TargetDir::Unknown => paths::search_parents_for_target_dir(), + }, + } + } +} + fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let ref target_dir = target_dir(); - let mut build = paths::cc_build(target_dir); + let ref prj = Project::init(); + let mut build = paths::cc_build(prj); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - write_header(target_dir); + write_header(prj); for path in rust_source_files { - generate_bridge(&mut build, path.as_ref(), target_dir)?; + generate_bridge(prj, &mut build, path.as_ref())?; } Ok(build) } -fn write_header(target_dir: &TargetDir) { - let ref cxx_h = paths::include_dir(target_dir).join("rust").join("cxx.h"); +fn write_header(prj: &Project) { + let ref cxx_h = paths::include_dir(prj).join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); } -fn generate_bridge( - build: &mut Build, - rust_source_file: &Path, - target_dir: &TargetDir, -) -> Result<()> { +fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); - let header_path = paths::out_with_extension(rust_source_file, ".h", target_dir)?; + let header_path = paths::out_with_extension(prj, rust_source_file, ".h")?; fs::create_dir_all(header_path.parent().unwrap())?; write(&header_path, &generated.header)?; - paths::symlink_header(&header_path, rust_source_file, target_dir); + paths::symlink_header(prj, &header_path, rust_source_file); - let implementation_path = paths::out_with_extension(rust_source_file, ".cc", target_dir)?; + let implementation_path = paths::out_with_extension(prj, rust_source_file, ".cc")?; write(&implementation_path, &generated.implementation)?; build.file(&implementation_path); Ok(()) @@ -151,11 +163,3 @@ fn write(path: &Path, content: &[u8]) -> Result<()> { fs::write(path, content)?; Ok(()) } - -fn target_dir() -> TargetDir { - match cargo::target_dir() { - target_dir @ TargetDir::Path(_) => target_dir, - // Fallback if Cargo did not work. - TargetDir::Unknown => paths::search_parents_for_target_dir(), - } -} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 1814f82..45df2a5 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,5 +1,6 @@ use crate::error::{Error, Result}; use crate::gen::fs; +use crate::Project; use std::env; use std::path::{Path, PathBuf}; @@ -14,10 +15,10 @@ fn out_dir() -> Result { .ok_or(Error::MissingOutDir) } -pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { +pub(crate) fn cc_build(prj: &Project) -> cc::Build { let mut build = cc::Build::new(); - build.include(include_dir(target_dir)); - if let TargetDir::Path(target_dir) = target_dir { + build.include(include_dir(prj)); + if let TargetDir::Path(target_dir) = &prj.target_dir { build.include(target_dir.parent().unwrap()); } build @@ -25,16 +26,16 @@ pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { // Symlink the header file into a predictable place. The header generated from // path/to/mod.rs gets linked to target/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) { - if let TargetDir::Unknown = target_dir { +pub(crate) fn symlink_header(prj: &Project, path: &Path, original: &Path) { + if let TargetDir::Unknown = prj.target_dir { return; } - let _ = try_symlink_header(path, original, target_dir); + let _ = try_symlink_header(prj, path, original); } -fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(original, target_dir)?; - let ref dst = include_dir(target_dir).join(suffix); +fn try_symlink_header(prj: &Project, path: &Path, original: &Path) -> Result<()> { + let suffix = relative_to_parent_of_target_dir(prj, original)?; + let ref dst = include_dir(prj).join(suffix); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); @@ -48,8 +49,8 @@ fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> R Ok(()) } -fn relative_to_parent_of_target_dir(original: &Path, target_dir: &TargetDir) -> Result { - let mut outer = match target_dir { +fn relative_to_parent_of_target_dir(prj: &Project, original: &Path) -> Result { + let mut outer = match &prj.target_dir { TargetDir::Path(target_dir) => target_dir.parent().unwrap(), TargetDir::Unknown => unimplemented!(), // FIXME }; @@ -65,21 +66,17 @@ fn relative_to_parent_of_target_dir(original: &Path, target_dir: &TargetDir) -> } } -pub(crate) fn out_with_extension( - path: &Path, - ext: &str, - target_dir: &TargetDir, -) -> Result { +pub(crate) fn out_with_extension(prj: &Project, path: &Path, ext: &str) -> Result { let mut file_name = path.file_name().unwrap().to_owned(); file_name.push(ext); let out_dir = out_dir()?; - let rel = relative_to_parent_of_target_dir(path, target_dir)?; + let rel = relative_to_parent_of_target_dir(prj, path)?; Ok(out_dir.join(rel).with_file_name(file_name)) } -pub(crate) fn include_dir(target_dir: &TargetDir) -> PathBuf { - match target_dir { +pub(crate) fn include_dir(prj: &Project) -> PathBuf { + match &prj.target_dir { TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), TargetDir::Unknown => out_dir().unwrap().join("cxxbridge"), // FIXME no unwrap } From 502d4d407ea5a9287a61dbf78cc8a8cbe217076c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 806/2232] Include out dir in Project --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 02df064..0e77088 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -66,7 +66,7 @@ use crate::paths::TargetDir; use cc::Build; use std::io::{self, Write}; use std::iter; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process; /// This returns a [`cc::Build`] on which you should continue to set up any @@ -99,23 +99,29 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } struct Project { + out_dir: PathBuf, target_dir: TargetDir, } impl Project { - fn init() -> Self { - Project { - target_dir: match cargo::target_dir() { - target_dir @ TargetDir::Path(_) => target_dir, - // Fallback if Cargo did not work. - TargetDir::Unknown => paths::search_parents_for_target_dir(), - }, - } + fn init() -> Result { + let out_dir = paths::out_dir()?; + + let target_dir = match cargo::target_dir() { + target_dir @ TargetDir::Path(_) => target_dir, + // Fallback if Cargo did not work. + TargetDir::Unknown => paths::search_parents_for_target_dir(), + }; + + Ok(Project { + out_dir, + target_dir, + }) } } fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let ref prj = Project::init(); + let ref prj = Project::init()?; let mut build = paths::cc_build(prj); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 45df2a5..47ceecc 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -9,7 +9,7 @@ pub(crate) enum TargetDir { Unknown, } -fn out_dir() -> Result { +pub(crate) fn out_dir() -> Result { env::var_os("OUT_DIR") .map(PathBuf::from) .ok_or(Error::MissingOutDir) From bc2c8e1f4daa6f4c512a5d8d4563dd0009d0aa74 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 807/2232] Access out dir through project struct --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 0e77088..27f7a8d 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -110,7 +110,7 @@ impl Project { let target_dir = match cargo::target_dir() { target_dir @ TargetDir::Path(_) => target_dir, // Fallback if Cargo did not work. - TargetDir::Unknown => paths::search_parents_for_target_dir(), + TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), }; Ok(Project { diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 47ceecc..e49dddb 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -70,20 +70,19 @@ pub(crate) fn out_with_extension(prj: &Project, path: &Path, ext: &str) -> Resul let mut file_name = path.file_name().unwrap().to_owned(); file_name.push(ext); - let out_dir = out_dir()?; let rel = relative_to_parent_of_target_dir(prj, path)?; - Ok(out_dir.join(rel).with_file_name(file_name)) + Ok(prj.out_dir.join(rel).with_file_name(file_name)) } pub(crate) fn include_dir(prj: &Project) -> PathBuf { match &prj.target_dir { TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), - TargetDir::Unknown => out_dir().unwrap().join("cxxbridge"), // FIXME no unwrap + TargetDir::Unknown => prj.out_dir.join("cxxbridge"), } } -pub(crate) fn search_parents_for_target_dir() -> TargetDir { - let mut dir = match out_dir().and_then(canonicalize) { +pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { + let mut dir = match out_dir.canonicalize() { Ok(dir) => dir, Err(_) => return TargetDir::Unknown, }; From ebe05fbd9188cdec78ed695497f08f0605505fac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 808/2232] Remove redundant create_dir_all This one is already handled by the following call to `write`. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 27f7a8d..5882191 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -144,7 +144,6 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let generated = gen::generate_from_path(rust_source_file, &opt); let header_path = paths::out_with_extension(prj, rust_source_file, ".h")?; - fs::create_dir_all(header_path.parent().unwrap())?; write(&header_path, &generated.header)?; paths::symlink_header(prj, &header_path, rust_source_file); From 1bd7a7088b936e1f7feeedfb7a42920de61c0188 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 809/2232] Propagate create dir error if subsequent write fails --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 5882191..ef075c1 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,7 +59,7 @@ mod gen; mod paths; mod syntax; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::{fs, Opt}; use crate::paths::TargetDir; @@ -154,6 +154,7 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> } fn write(path: &Path, content: &[u8]) -> Result<()> { + let mut create_dir_error = None; if path.exists() { if let Ok(existing) = fs::read(path) { if existing == content { @@ -163,8 +164,14 @@ fn write(path: &Path, content: &[u8]) -> Result<()> { } let _ = fs::remove_file(path); } else { - let _ = fs::create_dir_all(path.parent().unwrap()); + let parent = path.parent().unwrap(); + create_dir_error = fs::create_dir_all(parent).err(); + } + + match fs::write(path, content) { + // As long as write succeeded, ignore any create_dir_all error. + Ok(()) => Ok(()), + // If create_dir_all and write both failed, prefer the first error. + Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), } - fs::write(path, content)?; - Ok(()) } From b7ba747b5cd147a5ef6cd02eb59877b4352fbd3a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 810/2232] Only assume target dir if parent contains Cargo.toml --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index e49dddb..f05a927 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -87,7 +87,9 @@ pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { Err(_) => return TargetDir::Unknown, }; loop { - if dir.ends_with("target") { + let is_target = dir.ends_with("target"); + let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists(); + if is_target && parent_contains_cargo_toml { return TargetDir::Path(dir); } if !dir.pop() { From c0b3d9f055a91bc73fd541bc415e18a98fd46978 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 811/2232] Try finding target dir without canonicalize --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index f05a927..0145a38 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -82,19 +82,25 @@ pub(crate) fn include_dir(prj: &Project) -> PathBuf { } pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { - let mut dir = match out_dir.canonicalize() { - Ok(dir) => dir, - Err(_) => return TargetDir::Unknown, - }; + let mut dir = out_dir.to_owned(); + let mut also_try_canonical = true; loop { let is_target = dir.ends_with("target"); let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists(); if is_target && parent_contains_cargo_toml { return TargetDir::Path(dir); } - if !dir.pop() { - return TargetDir::Unknown; + if dir.pop() { + continue; + } + if also_try_canonical { + if let Ok(canonical_dir) = out_dir.canonicalize() { + dir = canonical_dir; + also_try_canonical = false; + continue; + } } + return TargetDir::Unknown; } } From 8079aa11cfaed5688d2a0bc9f4fdf1bb6c15c96a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 812/2232] Don't try canonicalize on windows --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 0145a38..f8b1ee9 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -82,8 +82,13 @@ pub(crate) fn include_dir(prj: &Project) -> PathBuf { } pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { + // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to + // handle in includes. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + let mut also_try_canonical = cfg!(not(windows)); + let mut dir = out_dir.to_owned(); - let mut also_try_canonical = true; loop { let is_target = dir.ends_with("target"); let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists(); From db44775d301868cb450ee74132df25ce522a04b8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:39 +0000 Subject: [PATCH 813/2232] Use relative paths as provided by caller's build.rs --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index ef075c1..bdaf4f6 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -143,11 +143,11 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); - let header_path = paths::out_with_extension(prj, rust_source_file, ".h")?; + let header_path = paths::out_with_extension(prj, rust_source_file, ".h"); write(&header_path, &generated.header)?; paths::symlink_header(prj, &header_path, rust_source_file); - let implementation_path = paths::out_with_extension(prj, rust_source_file, ".cc")?; + let implementation_path = paths::out_with_extension(prj, rust_source_file, ".cc"); write(&implementation_path, &generated.implementation)?; build.file(&implementation_path); Ok(()) diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index f8b1ee9..fc06157 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -34,8 +34,7 @@ pub(crate) fn symlink_header(prj: &Project, path: &Path, original: &Path) { } fn try_symlink_header(prj: &Project, path: &Path, original: &Path) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(prj, original)?; - let ref dst = include_dir(prj).join(suffix); + let ref dst = include_dir(prj).join(original); fs::create_dir_all(dst.parent().unwrap())?; let _ = fs::remove_file(dst); @@ -49,29 +48,14 @@ fn try_symlink_header(prj: &Project, path: &Path, original: &Path) -> Result<()> Ok(()) } -fn relative_to_parent_of_target_dir(prj: &Project, original: &Path) -> Result { - let mut outer = match &prj.target_dir { - TargetDir::Path(target_dir) => target_dir.parent().unwrap(), - TargetDir::Unknown => unimplemented!(), // FIXME - }; - let original = canonicalize(original)?; - loop { - if let Ok(suffix) = original.strip_prefix(outer) { - return Ok(suffix.to_owned()); - } - match outer.parent() { - Some(parent) => outer = parent, - None => return Ok(original.components().skip(1).collect()), - } - } -} - -pub(crate) fn out_with_extension(prj: &Project, path: &Path, ext: &str) -> Result { - let mut file_name = path.file_name().unwrap().to_owned(); +pub(crate) fn out_with_extension(prj: &Project, rel_path: &Path, ext: &str) -> PathBuf { + let mut file_name = rel_path.file_name().unwrap().to_owned(); file_name.push(ext); - let rel = relative_to_parent_of_target_dir(prj, path)?; - Ok(prj.out_dir.join(rel).with_file_name(file_name)) + prj.out_dir + .join("cxxbridge") + .join(rel_path) + .with_file_name(file_name) } pub(crate) fn include_dir(prj: &Project) -> PathBuf { @@ -109,20 +93,6 @@ pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { } } -#[cfg(not(windows))] -fn canonicalize(path: impl AsRef) -> Result { - Ok(fs::canonicalize(path)?) -} - -#[cfg(windows)] -fn canonicalize(path: impl AsRef) -> Result { - // Real fs::canonicalize on Windows produces UNC paths which cl.exe is - // unable to handle in includes. Use a poor approximation instead. - // https://github.com/rust-lang/rust/issues/42869 - // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(fs::current_dir()?.join(path)) -} - #[cfg(unix)] use self::fs::symlink_file as symlink_or_copy; diff --git a/gen/src/fs.rs b/gen/src/fs.rs index 77488f8..fe15f86 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -34,14 +34,6 @@ macro_rules! err { } } -pub(crate) fn canonicalize(path: impl AsRef) -> Result { - let path = path.as_ref(); - match std::fs::canonicalize(path) { - Ok(string) => Ok(string), - Err(e) => err!(e, "Unable to canonicalize path: `{}`", path), - } -} - pub(crate) fn copy(from: impl AsRef, to: impl AsRef) -> Result { let from = from.as_ref(); let to = to.as_ref(); From ba220ca7af20e71abed0f184a97a7ab155b0942d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 814/2232] Include package names in include path --- diff --git a/demo/Cargo.toml b/demo/Cargo.toml index d2147ab..dc94861 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cxxbridge-demo" +name = "demo" version = "0.0.0" authors = ["David Tolnay "] edition = "2018" diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index fc06157..007c5f1 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -2,6 +2,7 @@ use crate::error::{Error, Result}; use crate::gen::fs; use crate::Project; use std::env; +use std::ffi::OsString; use std::path::{Path, PathBuf}; pub(crate) enum TargetDir { @@ -34,11 +35,14 @@ pub(crate) fn symlink_header(prj: &Project, path: &Path, original: &Path) { } fn try_symlink_header(prj: &Project, path: &Path, original: &Path) -> Result<()> { - let ref dst = include_dir(prj).join(original); + let mut dst = include_dir(prj); + dst.extend(package_name()); + dst.push(original); - fs::create_dir_all(dst.parent().unwrap())?; - let _ = fs::remove_file(dst); - symlink_or_copy(path, dst)?; + let parent = dst.parent().unwrap(); + fs::create_dir_all(parent)?; + let _ = fs::remove_file(&dst); + symlink_or_copy(path, &dst)?; let mut file_name = dst.file_name().unwrap().to_os_string(); file_name.push(".h"); @@ -52,10 +56,11 @@ pub(crate) fn out_with_extension(prj: &Project, rel_path: &Path, ext: &str) -> P let mut file_name = rel_path.file_name().unwrap().to_owned(); file_name.push(ext); - prj.out_dir - .join("cxxbridge") - .join(rel_path) - .with_file_name(file_name) + let mut res = prj.out_dir.clone(); + res.push("cxxbridge"); + res.extend(package_name()); + res.push(rel_path); + res.with_file_name(file_name) } pub(crate) fn include_dir(prj: &Project) -> PathBuf { @@ -65,6 +70,10 @@ pub(crate) fn include_dir(prj: &Project) -> PathBuf { } } +fn package_name() -> Option { + env::var_os("CARGO_PKG_NAME") +} + pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to // handle in includes. From 65c0ac885bc777a27fe4c03dc4b2ceed48260548 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 815/2232] Remove "parent of target dir" from include path --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 007c5f1..8fe86ae 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -19,9 +19,6 @@ pub(crate) fn out_dir() -> Result { pub(crate) fn cc_build(prj: &Project) -> cc::Build { let mut build = cc::Build::new(); build.include(include_dir(prj)); - if let TargetDir::Path(target_dir) = &prj.target_dir { - build.include(target_dir.parent().unwrap()); - } build } From 2f40fc93e6b305d45889b13a0e74823419da1c41 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 816/2232] Include manifest dir --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 8fe86ae..9d922ed 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -19,6 +19,9 @@ pub(crate) fn out_dir() -> Result { pub(crate) fn cc_build(prj: &Project) -> cc::Build { let mut build = cc::Build::new(); build.include(include_dir(prj)); + if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") { + build.include(manifest_dir); + } build } From 421a250d7808f592ffbb08e733d0c16b96dd48ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 817/2232] Prefix crate name to the import paths --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index bdaf4f6..88a1874 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -126,6 +126,7 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate write_header(prj); + symlink_crate(prj, &mut build); for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; @@ -135,10 +136,28 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul } fn write_header(prj: &Project) { - let ref cxx_h = paths::include_dir(prj).join("rust").join("cxx.h"); + let include_dir = paths::include_dir(prj); + let ref cxx_h = include_dir.join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); } +fn symlink_crate(prj: &Project, build: &mut Build) { + let manifest_dir = match paths::manifest_dir() { + Some(manifest_dir) => manifest_dir, + None => return, + }; + let package_name = match paths::package_name() { + Some(package_name) => package_name, + None => return, + }; + + let mut link = paths::include_dir(prj); + link.push("CRATE"); + let _ = fs::create_dir_all(&link); + let _ = paths::symlink_dir(manifest_dir, link.join(package_name)); + build.include(link); +} + fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 9d922ed..e02246a 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -19,9 +19,6 @@ pub(crate) fn out_dir() -> Result { pub(crate) fn cc_build(prj: &Project) -> cc::Build { let mut build = cc::Build::new(); build.include(include_dir(prj)); - if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") { - build.include(manifest_dir); - } build } @@ -70,7 +67,11 @@ pub(crate) fn include_dir(prj: &Project) -> PathBuf { } } -fn package_name() -> Option { +pub(crate) fn manifest_dir() -> Option { + env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) +} + +pub(crate) fn package_name() -> Option { env::var_os("CARGO_PKG_NAME") } @@ -117,3 +118,11 @@ fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { #[cfg(not(any(unix, windows)))] use self::fs::copy as symlink_or_copy; + +#[cfg(any(unix, windows))] +pub(crate) use self::fs::symlink_dir; + +#[cfg(not(any(unix, windows)))] +pub(crate) fn symlink_dir(_src: impl AsRef, _dst: impl AsRef) -> Result<()> { + Ok(()) +} From dd24f74dfcf1990a3f74988a95c148b0e80efbcc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 818/2232] Move cc-related code out of paths module --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 88a1874..c611eea 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -122,9 +122,10 @@ impl Project { fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; - let mut build = paths::cc_build(prj); + let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate + build.include(paths::include_dir(prj)); write_header(prj); symlink_crate(prj, &mut build); diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index e02246a..d6ff359 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -16,12 +16,6 @@ pub(crate) fn out_dir() -> Result { .ok_or(Error::MissingOutDir) } -pub(crate) fn cc_build(prj: &Project) -> cc::Build { - let mut build = cc::Build::new(); - build.include(include_dir(prj)); - build -} - // Symlink the header file into a predictable place. The header generated from // path/to/mod.rs gets linked to target/cxxbridge/path/to/mod.rs.h. pub(crate) fn symlink_header(prj: &Project, path: &Path, original: &Path) { From 8f18318902f205002b13ea17b3bf53a712f3c879 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 819/2232] Guard our path joins from going outside out-dir --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index c611eea..fa7b825 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -62,7 +62,7 @@ mod syntax; use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::{fs, Opt}; -use crate::paths::TargetDir; +use crate::paths::{PathExt, TargetDir}; use cc::Build; use std::io::{self, Write}; use std::iter; @@ -162,14 +162,21 @@ fn symlink_crate(prj: &Project, build: &mut Build) { fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); + let ref rel_path = paths::local_relative_path(rust_source_file); + + let ref rel_path_h = rel_path.with_appended_extension(".h"); + let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); + write(header_path, &generated.header)?; + paths::symlink_namespaced(header_path, &prj.out_dir, rel_path); + if let TargetDir::Path(target_dir) = &prj.target_dir { + paths::symlink_namespaced(header_path, target_dir, rel_path); + paths::symlink_namespaced(header_path, target_dir, rel_path_h); + } - let header_path = paths::out_with_extension(prj, rust_source_file, ".h"); - write(&header_path, &generated.header)?; - paths::symlink_header(prj, &header_path, rust_source_file); - - let implementation_path = paths::out_with_extension(prj, rust_source_file, ".cc"); - write(&implementation_path, &generated.implementation)?; - build.file(&implementation_path); + let ref rel_path_cc = rel_path.with_appended_extension(".cc"); + let ref implementation_path = paths::namespaced(&prj.out_dir, rel_path_cc); + write(implementation_path, &generated.implementation)?; + build.file(implementation_path); Ok(()) } diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index d6ff359..1a423dc 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -2,8 +2,8 @@ use crate::error::{Error, Result}; use crate::gen::fs; use crate::Project; use std::env; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; +use std::ffi::{OsStr, OsString}; +use std::path::{Component, Path, PathBuf}; pub(crate) enum TargetDir { Path(PathBuf), @@ -16,42 +16,44 @@ pub(crate) fn out_dir() -> Result { .ok_or(Error::MissingOutDir) } -// Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to target/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(prj: &Project, path: &Path, original: &Path) { - if let TargetDir::Unknown = prj.target_dir { - return; +// Given a path provided by the user, determines where generated files related +// to that path should go in our out dir. In particular we don't want to +// accidentally write generated code upward of our out dir, even if the user +// passed a path containing lots of `..` or an absolute path. +pub(crate) fn local_relative_path(path: &Path) -> PathBuf { + let mut rel_path = PathBuf::new(); + for component in path.components() { + match component { + Component::Prefix(_) | Component::RootDir | Component::CurDir => {} + Component::ParentDir => drop(rel_path.pop()), // noop if empty + Component::Normal(name) => rel_path.push(name), + } } - let _ = try_symlink_header(prj, path, original); + rel_path } -fn try_symlink_header(prj: &Project, path: &Path, original: &Path) -> Result<()> { - let mut dst = include_dir(prj); - dst.extend(package_name()); - dst.push(original); - - let parent = dst.parent().unwrap(); - fs::create_dir_all(parent)?; - let _ = fs::remove_file(&dst); - symlink_or_copy(path, &dst)?; - - let mut file_name = dst.file_name().unwrap().to_os_string(); - file_name.push(".h"); - let ref dst2 = dst.with_file_name(file_name); - symlink_or_copy(path, dst2)?; +pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf { + let mut path = base.to_owned(); + path.push("cxxbridge"); + path.extend(package_name()); + path.push(rel_path); + path +} - Ok(()) +pub(crate) fn symlink_namespaced(src: &Path, base: &Path, rel_path: &Path) { + let _ = symlink_or_copy(src, namespaced(base, rel_path)); } -pub(crate) fn out_with_extension(prj: &Project, rel_path: &Path, ext: &str) -> PathBuf { - let mut file_name = rel_path.file_name().unwrap().to_owned(); - file_name.push(ext); +pub(crate) trait PathExt { + fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; +} - let mut res = prj.out_dir.clone(); - res.push("cxxbridge"); - res.extend(package_name()); - res.push(rel_path); - res.with_file_name(file_name) +impl PathExt for Path { + fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf { + let mut file_name = self.file_name().unwrap().to_owned(); + file_name.push(suffix); + self.with_file_name(file_name) + } } pub(crate) fn include_dir(prj: &Project) -> PathBuf { From 63b34b8981579ddd2bd04ed80b81b5c3415e3acd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:24:40 +0000 Subject: [PATCH 820/2232] Write cxx.h to out dir and target dir --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index fa7b825..a36f925 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -137,9 +137,14 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul } fn write_header(prj: &Project) { - let include_dir = paths::include_dir(prj); - let ref cxx_h = include_dir.join("rust").join("cxx.h"); + let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + if let TargetDir::Path(target_dir) = &prj.target_dir { + let ref header_dir = target_dir.join("cxxbridge").join("rust"); + let _ = fs::create_dir_all(header_dir); + let ref cxx_h = header_dir.join("cxx.h"); + let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + } } fn symlink_crate(prj: &Project, build: &mut Build) { From 825ad27691dfba1edbe8401686bd6a7ed8f51d73 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:37:02 +0000 Subject: [PATCH 821/2232] Create dir for the links in the target dir --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index a36f925..9a1ad0a 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -174,8 +174,10 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> write(header_path, &generated.header)?; paths::symlink_namespaced(header_path, &prj.out_dir, rel_path); if let TargetDir::Path(target_dir) = &prj.target_dir { - paths::symlink_namespaced(header_path, target_dir, rel_path); - paths::symlink_namespaced(header_path, target_dir, rel_path_h); + let ref link_path = paths::namespaced(target_dir, rel_path); + let _ = fs::create_dir_all(link_path.parent().unwrap()); + let _ = paths::symlink_or_copy(header_path, link_path); + let _ = paths::symlink_or_copy(header_path, link_path.with_appended_extension(".h")); } let ref rel_path_cc = rel_path.with_appended_extension(".cc"); diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 1a423dc..c3238b3 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -100,10 +100,10 @@ pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { } #[cfg(unix)] -use self::fs::symlink_file as symlink_or_copy; +pub(crate) use self::fs::symlink_file as symlink_or_copy; #[cfg(windows)] -fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { +pub(crate) fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. if fs::symlink_file(src, dst).is_err() { @@ -113,7 +113,7 @@ fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { } #[cfg(not(any(unix, windows)))] -use self::fs::copy as symlink_or_copy; +pub(crate) use self::fs::copy as symlink_or_copy; #[cfg(any(unix, windows))] pub(crate) use self::fs::symlink_dir; From 913067940435431c95a6a42f2447b24d0958742e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 04:38:15 +0000 Subject: [PATCH 822/2232] Inline once used symlink function --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 9a1ad0a..b42bbe0 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -172,7 +172,9 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let ref rel_path_h = rel_path.with_appended_extension(".h"); let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); write(header_path, &generated.header)?; - paths::symlink_namespaced(header_path, &prj.out_dir, rel_path); + + let ref link_path = paths::namespaced(&prj.out_dir, rel_path); + let _ = paths::symlink_or_copy(header_path, link_path); if let TargetDir::Path(target_dir) = &prj.target_dir { let ref link_path = paths::namespaced(target_dir, rel_path); let _ = fs::create_dir_all(link_path.parent().unwrap()); diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index c3238b3..5f6243f 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -40,10 +40,6 @@ pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf { path } -pub(crate) fn symlink_namespaced(src: &Path, base: &Path, rel_path: &Path) { - let _ = symlink_or_copy(src, namespaced(base, rel_path)); -} - pub(crate) trait PathExt { fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; } From 6eeaeef36caf7e41e6c7481d96b36bae3c0ca882 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:14:03 +0000 Subject: [PATCH 823/2232] Run cargo metadata from top level crate's dir When Cargo runs a build script, its current directory is set to the manifest directory of the crate whose build script it is. That means if crate A depends on crate B depends on cxx-build, the previous logic would have found B's target dir (somewhere in the Cargo registry cache) rather than the intended A target dir being used for the current build. --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 6a4f1a6..b67c21b 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -1,12 +1,13 @@ use crate::paths::TargetDir; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -pub(crate) fn target_dir() -> TargetDir { +pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { (|| { let cargo = option_env!("CARGO").unwrap_or("cargo"); let output = Command::new(cargo) + .current_dir(out_dir) .arg("metadata") .arg("--no-deps") .arg("--format-version=1") diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index b42bbe0..1628510 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -107,7 +107,7 @@ impl Project { fn init() -> Result { let out_dir = paths::out_dir()?; - let target_dir = match cargo::target_dir() { + let target_dir = match cargo::target_dir(&out_dir) { target_dir @ TargetDir::Path(_) => target_dir, // Fallback if Cargo did not work. TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), From 25db1555f5211c4ff09c65a5c69a266a41e86cb9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:41:15 +0000 Subject: [PATCH 824/2232] Update import path scheme for tests --- diff --git a/tests/BUCK b/tests/BUCK index 4bfc9e8..a96bfb8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -24,9 +24,10 @@ cxx_library( ":gen-lib-source", ":gen-module-source", ], + header_namespace = "cxx-test-suite", headers = { - "ffi/lib.rs.h": ":gen-lib-header", - "ffi/tests.h": "ffi/tests.h", + "lib.rs.h": ":gen-lib-header", + "tests.h": "ffi/tests.h", }, deps = ["//:core"], ) diff --git a/tests/BUILD b/tests/BUILD index e1f1637..29cc79c 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -26,6 +26,8 @@ cc_library( ":gen-module-source", ], hdrs = ["ffi/tests.h"], + include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", deps = [ ":lib-include", "//:core", @@ -51,7 +53,7 @@ genrule( cc_library( name = "lib-include", hdrs = [":gen-lib-header"], - include_prefix = "tests/ffi", + include_prefix = "cxx-test-suite", ) genrule( diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 8aaac75..d6724f5 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -23,7 +23,7 @@ pub mod ffi { } extern "C" { - include!("tests/ffi/tests.h"); + include!("cxx-test-suite/tests.h"); type C; diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 77bae06..8862dc1 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -4,7 +4,7 @@ #[cxx::bridge(namespace = tests)] pub mod ffi { extern "C" { - include!("tests/ffi/tests.h"); + include!("cxx-test-suite/tests.h"); type C = crate::ffi::C; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d45d5d5..8dd16e8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ -#include "tests/ffi/tests.h" -#include "tests/ffi/lib.rs.h" +#include "cxx-test-suite/tests.h" +#include "cxx-test-suite/lib.rs.h" #include #include #include From 70d476cd875f77622680e612f90a6b09a02b3b94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:45:36 +0000 Subject: [PATCH 825/2232] Match symlink_file's signature on windows --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 5f6243f..96e3708 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -99,9 +99,11 @@ pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { pub(crate) use self::fs::symlink_file as symlink_or_copy; #[cfg(windows)] -pub(crate) fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { +pub(crate) fn symlink_or_copy(src: impl AsRef, dst: impl AsRef) -> Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. + let src = src.as_ref(); + let dst = dst.as_ref(); if fs::symlink_file(src, dst).is_err() { fs::copy(src, dst)?; } From 759be0fabecdf21099ecc93504ba914ed88b15e6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:46:14 +0000 Subject: [PATCH 826/2232] Regenerate Cargo.lock for renamed demo crate --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1f3e760..b335d31 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -115,14 +115,6 @@ dependencies = [ ] [[package]] -name = "cxxbridge-demo" -version = "0.0.0" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] name = "cxxbridge-flags" version = "0.3.9" @@ -137,6 +129,14 @@ dependencies = [ ] [[package]] +name = "demo" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] name = "dissimilar" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" From 34e603ea5e9d38cd2b561d498b5faf101244101b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:57:22 +0000 Subject: [PATCH 827/2232] Merge pull request #276 from dtolnay/build Rewrite cxx-build crate --- diff --git a/demo/Cargo.toml b/demo/Cargo.toml index d2147ab..dc94861 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cxxbridge-demo" +name = "demo" version = "0.0.0" authors = ["David Tolnay "] edition = "2018" diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 3bddc6e..b67c21b 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -1,19 +1,19 @@ -use crate::error::TargetDirError; use crate::paths::TargetDir; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use std::str; -pub(crate) fn target_dir() -> Result { - let cargo = option_env!("CARGO").unwrap_or("cargo"); - let output = Command::new(cargo) - .arg("metadata") - .arg("--no-deps") - .arg("--format-version=1") - .output() - .map_err(TargetDirError::Io)?; - +pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { (|| { + let cargo = option_env!("CARGO").unwrap_or("cargo"); + let output = Command::new(cargo) + .current_dir(out_dir) + .arg("metadata") + .arg("--no-deps") + .arg("--format-version=1") + .output() + .ok()?; + // Cargo only outputs utf8 encoded JSON. let mut metadata = str::from_utf8(&output.stdout).ok()?; @@ -25,7 +25,7 @@ pub(crate) fn target_dir() -> Result { let close_quote_index = metadata.find('"')?; let string = &metadata[..close_quote_index]; let target_directory = string.replace("\\\\", "\\"); - Some(TargetDir(PathBuf::from(target_directory))) + Some(TargetDir::Path(PathBuf::from(target_directory))) })() - .ok_or(TargetDirError::NotFound) + .unwrap_or(TargetDir::Unknown) } diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index ed4c6af..0ed4d74 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,28 +1,19 @@ use crate::gen::fs; use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { MissingOutDir, - TargetDir(TargetDirError), Fs(fs::Error), } -#[derive(Debug)] -pub(crate) enum TargetDirError { - Io(io::Error), - NotFound, -} - impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), - Error::TargetDir(_) => write!(f, "unable to identify target dir"), Error::Fs(err) => err.fmt(f), } } @@ -31,10 +22,6 @@ impl Display for Error { impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Error::TargetDir(err) => match err { - TargetDirError::Io(err) => Some(err), - TargetDirError::NotFound => None, - }, Error::Fs(err) => err.source(), _ => None, } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 22f53f6..1628510 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -59,14 +59,14 @@ mod gen; mod paths; mod syntax; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::{fs, Opt}; -use crate::paths::TargetDir; +use crate::paths::{PathExt, TargetDir}; use cc::Build; use std::io::{self, Write}; use std::iter; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process; /// This returns a [`cc::Build`] on which you should continue to set up any @@ -98,45 +98,99 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> }) } +struct Project { + out_dir: PathBuf, + target_dir: TargetDir, +} + +impl Project { + fn init() -> Result { + let out_dir = paths::out_dir()?; + + let target_dir = match cargo::target_dir(&out_dir) { + target_dir @ TargetDir::Path(_) => target_dir, + // Fallback if Cargo did not work. + TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), + }; + + Ok(Project { + out_dir, + target_dir, + }) + } +} + fn build(rust_source_files: &mut dyn Iterator>) -> Result { - let ref target_dir = paths::target_dir()?; - let mut build = paths::cc_build(target_dir); + let ref prj = Project::init()?; + let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - write_header(target_dir); + build.include(paths::include_dir(prj)); + write_header(prj); + symlink_crate(prj, &mut build); for path in rust_source_files { - generate_bridge(&mut build, path.as_ref(), target_dir)?; + generate_bridge(prj, &mut build, path.as_ref())?; } Ok(build) } -fn write_header(target_dir: &TargetDir) { - let ref cxx_h = paths::include_dir(target_dir).join("rust").join("cxx.h"); +fn write_header(prj: &Project) { + let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + if let TargetDir::Path(target_dir) = &prj.target_dir { + let ref header_dir = target_dir.join("cxxbridge").join("rust"); + let _ = fs::create_dir_all(header_dir); + let ref cxx_h = header_dir.join("cxx.h"); + let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + } } -fn generate_bridge( - build: &mut Build, - rust_source_file: &Path, - target_dir: &TargetDir, -) -> Result<()> { +fn symlink_crate(prj: &Project, build: &mut Build) { + let manifest_dir = match paths::manifest_dir() { + Some(manifest_dir) => manifest_dir, + None => return, + }; + let package_name = match paths::package_name() { + Some(package_name) => package_name, + None => return, + }; + + let mut link = paths::include_dir(prj); + link.push("CRATE"); + let _ = fs::create_dir_all(&link); + let _ = paths::symlink_dir(manifest_dir, link.join(package_name)); + build.include(link); +} + +fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt::default(); let generated = gen::generate_from_path(rust_source_file, &opt); + let ref rel_path = paths::local_relative_path(rust_source_file); + + let ref rel_path_h = rel_path.with_appended_extension(".h"); + let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); + write(header_path, &generated.header)?; - let header_path = paths::out_with_extension(rust_source_file, ".h", target_dir)?; - fs::create_dir_all(header_path.parent().unwrap())?; - write(&header_path, &generated.header)?; - paths::symlink_header(&header_path, rust_source_file, target_dir); + let ref link_path = paths::namespaced(&prj.out_dir, rel_path); + let _ = paths::symlink_or_copy(header_path, link_path); + if let TargetDir::Path(target_dir) = &prj.target_dir { + let ref link_path = paths::namespaced(target_dir, rel_path); + let _ = fs::create_dir_all(link_path.parent().unwrap()); + let _ = paths::symlink_or_copy(header_path, link_path); + let _ = paths::symlink_or_copy(header_path, link_path.with_appended_extension(".h")); + } - let implementation_path = paths::out_with_extension(rust_source_file, ".cc", target_dir)?; - write(&implementation_path, &generated.implementation)?; - build.file(&implementation_path); + let ref rel_path_cc = rel_path.with_appended_extension(".cc"); + let ref implementation_path = paths::namespaced(&prj.out_dir, rel_path_cc); + write(implementation_path, &generated.implementation)?; + build.file(implementation_path); Ok(()) } fn write(path: &Path, content: &[u8]) -> Result<()> { + let mut create_dir_error = None; if path.exists() { if let Ok(existing) = fs::read(path) { if existing == content { @@ -146,8 +200,14 @@ fn write(path: &Path, content: &[u8]) -> Result<()> { } let _ = fs::remove_file(path); } else { - let _ = fs::create_dir_all(path.parent().unwrap()); + let parent = path.parent().unwrap(); + create_dir_error = fs::create_dir_all(parent).err(); + } + + match fs::write(path, content) { + // As long as write succeeded, ignore any create_dir_all error. + Ok(()) => Ok(()), + // If create_dir_all and write both failed, prefer the first error. + Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), } - fs::write(path, content)?; - Ok(()) } diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 870c49d..96e3708 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,124 +1,109 @@ -use crate::cargo; use crate::error::{Error, Result}; use crate::gen::fs; +use crate::Project; use std::env; -use std::ops::Deref; -use std::path::{Path, PathBuf}; +use std::ffi::{OsStr, OsString}; +use std::path::{Component, Path, PathBuf}; -pub(crate) struct TargetDir(pub PathBuf); - -impl Deref for TargetDir { - type Target = Path; - fn deref(&self) -> &Self::Target { - &self.0 - } +pub(crate) enum TargetDir { + Path(PathBuf), + Unknown, } -fn out_dir() -> Result { +pub(crate) fn out_dir() -> Result { env::var_os("OUT_DIR") .map(PathBuf::from) .ok_or(Error::MissingOutDir) } -pub(crate) fn cc_build(target_dir: &TargetDir) -> cc::Build { - let mut build = cc::Build::new(); - build.include(include_dir(target_dir)); - build.include(target_dir.parent().unwrap()); - build +// Given a path provided by the user, determines where generated files related +// to that path should go in our out dir. In particular we don't want to +// accidentally write generated code upward of our out dir, even if the user +// passed a path containing lots of `..` or an absolute path. +pub(crate) fn local_relative_path(path: &Path) -> PathBuf { + let mut rel_path = PathBuf::new(); + for component in path.components() { + match component { + Component::Prefix(_) | Component::RootDir | Component::CurDir => {} + Component::ParentDir => drop(rel_path.pop()), // noop if empty + Component::Normal(name) => rel_path.push(name), + } + } + rel_path } -// Symlink the header file into a predictable place. The header generated from -// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h. -pub(crate) fn symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) { - let _ = try_symlink_header(path, original, target_dir); +pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf { + let mut path = base.to_owned(); + path.push("cxxbridge"); + path.extend(package_name()); + path.push(rel_path); + path } -fn try_symlink_header(path: &Path, original: &Path, target_dir: &TargetDir) -> Result<()> { - let suffix = relative_to_parent_of_target_dir(original, target_dir)?; - let ref dst = include_dir(target_dir).join(suffix); - - fs::create_dir_all(dst.parent().unwrap())?; - let _ = fs::remove_file(dst); - symlink_or_copy(path, dst)?; - - let mut file_name = dst.file_name().unwrap().to_os_string(); - file_name.push(".h"); - let ref dst2 = dst.with_file_name(file_name); - symlink_or_copy(path, dst2)?; +pub(crate) trait PathExt { + fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; +} - Ok(()) +impl PathExt for Path { + fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf { + let mut file_name = self.file_name().unwrap().to_owned(); + file_name.push(suffix); + self.with_file_name(file_name) + } } -fn relative_to_parent_of_target_dir(original: &Path, target_dir: &TargetDir) -> Result { - let mut outer = target_dir.parent().unwrap(); - let original = canonicalize(original)?; - loop { - if let Ok(suffix) = original.strip_prefix(outer) { - return Ok(suffix.to_owned()); - } - match outer.parent() { - Some(parent) => outer = parent, - None => return Ok(original.components().skip(1).collect()), - } +pub(crate) fn include_dir(prj: &Project) -> PathBuf { + match &prj.target_dir { + TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), + TargetDir::Unknown => prj.out_dir.join("cxxbridge"), } } -pub(crate) fn out_with_extension( - path: &Path, - ext: &str, - target_dir: &TargetDir, -) -> Result { - let mut file_name = path.file_name().unwrap().to_owned(); - file_name.push(ext); - - let out_dir = out_dir()?; - let rel = relative_to_parent_of_target_dir(path, target_dir)?; - Ok(out_dir.join(rel).with_file_name(file_name)) +pub(crate) fn manifest_dir() -> Option { + env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) } -pub(crate) fn include_dir(target_dir: &TargetDir) -> PathBuf { - target_dir.join("cxxbridge") +pub(crate) fn package_name() -> Option { + env::var_os("CARGO_PKG_NAME") } -pub(crate) fn target_dir() -> Result { - let fallback_err = match cargo::target_dir() { - Ok(target_dir) => return Ok(target_dir), - Err(err) => Error::TargetDir(err), - }; +pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { + // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to + // handle in includes. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + let mut also_try_canonical = cfg!(not(windows)); - // Fallback if Cargo did not work. - let mut dir = out_dir().and_then(canonicalize)?; + let mut dir = out_dir.to_owned(); loop { - if dir.ends_with("target") { - return Ok(TargetDir(dir)); + let is_target = dir.ends_with("target"); + let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists(); + if is_target && parent_contains_cargo_toml { + return TargetDir::Path(dir); } - if !dir.pop() { - return Err(fallback_err); + if dir.pop() { + continue; } + if also_try_canonical { + if let Ok(canonical_dir) = out_dir.canonicalize() { + dir = canonical_dir; + also_try_canonical = false; + continue; + } + } + return TargetDir::Unknown; } } -#[cfg(not(windows))] -fn canonicalize(path: impl AsRef) -> Result { - Ok(fs::canonicalize(path)?) -} - -#[cfg(windows)] -fn canonicalize(path: impl AsRef) -> Result { - // Real fs::canonicalize on Windows produces UNC paths which cl.exe is - // unable to handle in includes. Use a poor approximation instead. - // https://github.com/rust-lang/rust/issues/42869 - // https://github.com/alexcrichton/cc-rs/issues/169 - Ok(fs::current_dir()?.join(path)) -} - #[cfg(unix)] -use self::fs::symlink_file as symlink_or_copy; +pub(crate) use self::fs::symlink_file as symlink_or_copy; #[cfg(windows)] -fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { +pub(crate) fn symlink_or_copy(src: impl AsRef, dst: impl AsRef) -> Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. + let src = src.as_ref(); + let dst = dst.as_ref(); if fs::symlink_file(src, dst).is_err() { fs::copy(src, dst)?; } @@ -126,4 +111,12 @@ fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> { } #[cfg(not(any(unix, windows)))] -use self::fs::copy as symlink_or_copy; +pub(crate) use self::fs::copy as symlink_or_copy; + +#[cfg(any(unix, windows))] +pub(crate) use self::fs::symlink_dir; + +#[cfg(not(any(unix, windows)))] +pub(crate) fn symlink_dir(_src: impl AsRef, _dst: impl AsRef) -> Result<()> { + Ok(()) +} diff --git a/gen/src/fs.rs b/gen/src/fs.rs index 77488f8..fe15f86 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -34,14 +34,6 @@ macro_rules! err { } } -pub(crate) fn canonicalize(path: impl AsRef) -> Result { - let path = path.as_ref(); - match std::fs::canonicalize(path) { - Ok(string) => Ok(string), - Err(e) => err!(e, "Unable to canonicalize path: `{}`", path), - } -} - pub(crate) fn copy(from: impl AsRef, to: impl AsRef) -> Result { let from = from.as_ref(); let to = to.as_ref(); diff --git a/tests/BUCK b/tests/BUCK index 4bfc9e8..a96bfb8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -24,9 +24,10 @@ cxx_library( ":gen-lib-source", ":gen-module-source", ], + header_namespace = "cxx-test-suite", headers = { - "ffi/lib.rs.h": ":gen-lib-header", - "ffi/tests.h": "ffi/tests.h", + "lib.rs.h": ":gen-lib-header", + "tests.h": "ffi/tests.h", }, deps = ["//:core"], ) diff --git a/tests/BUILD b/tests/BUILD index e1f1637..29cc79c 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -26,6 +26,8 @@ cc_library( ":gen-module-source", ], hdrs = ["ffi/tests.h"], + include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", deps = [ ":lib-include", "//:core", @@ -51,7 +53,7 @@ genrule( cc_library( name = "lib-include", hdrs = [":gen-lib-header"], - include_prefix = "tests/ffi", + include_prefix = "cxx-test-suite", ) genrule( diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 8aaac75..d6724f5 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -23,7 +23,7 @@ pub mod ffi { } extern "C" { - include!("tests/ffi/tests.h"); + include!("cxx-test-suite/tests.h"); type C; diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 77bae06..8862dc1 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -4,7 +4,7 @@ #[cxx::bridge(namespace = tests)] pub mod ffi { extern "C" { - include!("tests/ffi/tests.h"); + include!("cxx-test-suite/tests.h"); type C = crate::ffi::C; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index d45d5d5..8dd16e8 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ -#include "tests/ffi/tests.h" -#include "tests/ffi/lib.rs.h" +#include "cxx-test-suite/tests.h" +#include "cxx-test-suite/lib.rs.h" #include #include #include diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1f3e760..b335d31 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -115,14 +115,6 @@ dependencies = [ ] [[package]] -name = "cxxbridge-demo" -version = "0.0.0" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] name = "cxxbridge-flags" version = "0.3.9" @@ -137,6 +129,14 @@ dependencies = [ ] [[package]] +name = "demo" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] name = "dissimilar" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" From 84f8cf973862311b293d540acc1bc3e74fb8ed15 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 05:58:35 +0000 Subject: [PATCH 828/2232] Unindent target dir cargo logic --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index b67c21b..35b6606 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -4,28 +4,29 @@ use std::process::Command; use std::str; pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { - (|| { - let cargo = option_env!("CARGO").unwrap_or("cargo"); - let output = Command::new(cargo) - .current_dir(out_dir) - .arg("metadata") - .arg("--no-deps") - .arg("--format-version=1") - .output() - .ok()?; + try_target_dir(out_dir).map_or(TargetDir::Unknown, TargetDir::Path) +} + +fn try_target_dir(out_dir: &Path) -> Option { + let cargo = option_env!("CARGO").unwrap_or("cargo"); + let output = Command::new(cargo) + .current_dir(out_dir) + .arg("metadata") + .arg("--no-deps") + .arg("--format-version=1") + .output() + .ok()?; - // Cargo only outputs utf8 encoded JSON. - let mut metadata = str::from_utf8(&output.stdout).ok()?; + // Cargo only outputs utf8 encoded JSON. + let mut metadata = str::from_utf8(&output.stdout).ok()?; - let key_pattern = "\"target_directory\":"; - let key_index = metadata.rfind(key_pattern)?; - metadata = &metadata[key_index + key_pattern.len()..]; - let open_quote_index = metadata.find('"')?; - metadata = &metadata[open_quote_index + 1..]; - let close_quote_index = metadata.find('"')?; - let string = &metadata[..close_quote_index]; - let target_directory = string.replace("\\\\", "\\"); - Some(TargetDir::Path(PathBuf::from(target_directory))) - })() - .unwrap_or(TargetDir::Unknown) + let key_pattern = "\"target_directory\":"; + let key_index = metadata.rfind(key_pattern)?; + metadata = &metadata[key_index + key_pattern.len()..]; + let open_quote_index = metadata.find('"')?; + metadata = &metadata[open_quote_index + 1..]; + let close_quote_index = metadata.find('"')?; + let string = &metadata[..close_quote_index]; + let target_directory = string.replace("\\\\", "\\"); + Some(PathBuf::from(target_directory)) } From 591dcb647d711d252519c61249116c13f06a3fa1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 06:00:38 +0000 Subject: [PATCH 829/2232] Bump namespace to 04 --- diff --git a/Cargo.toml b/Cargo.toml index 5e6fe27..2513633 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "cxx" version = "0.3.9" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" -links = "cxxbridge03" +links = "cxxbridge04" license = "MIT OR Apache-2.0" description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" diff --git a/build.rs b/build.rs index cecf0e5..502a60b 100644 --- a/build.rs +++ b/build.rs @@ -4,7 +4,7 @@ fn main() { .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate .flag_if_supported(cxxbridge_flags::STD) - .compile("cxxbridge03"); + .compile("cxxbridge04"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); println!("cargo:rustc-cfg=built_with_cargo"); diff --git a/gen/src/tests.rs b/gen/src/tests.rs index 76ff987..dbf4c62 100644 --- a/gen/src/tests.rs +++ b/gen/src/tests.rs @@ -21,7 +21,7 @@ fn test_cpp() { let output = std::str::from_utf8(&output.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. - assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); } #[test] @@ -34,5 +34,5 @@ fn test_annotation() { }; let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap(); let output = std::str::from_utf8(&output.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); } diff --git a/gen/src/write.rs b/gen/src/write.rs index ccb9f61..20c63a9 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -230,7 +230,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge03"); + out.begin_block("inline namespace cxxbridge04"); if needs_rust_string || needs_rust_str @@ -253,15 +253,15 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "struct unsafe_bitcopy_t;"); } - include::write(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING"); - include::write(out, needs_rust_str, "CXXBRIDGE03_RUST_STR"); - include::write(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE"); - include::write(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX"); - include::write(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC"); - include::write(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN"); - include::write(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR"); - include::write(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE"); - include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY"); + include::write(out, needs_rust_string, "CXXBRIDGE04_RUST_STRING"); + include::write(out, needs_rust_str, "CXXBRIDGE04_RUST_STR"); + include::write(out, needs_rust_slice, "CXXBRIDGE04_RUST_SLICE"); + include::write(out, needs_rust_box, "CXXBRIDGE04_RUST_BOX"); + include::write(out, needs_rust_vec, "CXXBRIDGE04_RUST_VEC"); + include::write(out, needs_rust_fn, "CXXBRIDGE04_RUST_FN"); + include::write(out, needs_rust_error, "CXXBRIDGE04_RUST_ERROR"); + include::write(out, needs_rust_isize, "CXXBRIDGE04_RUST_ISIZE"); + include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE04_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); @@ -287,7 +287,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } - out.end_block("namespace cxxbridge03"); + out.end_block("namespace cxxbridge04"); if needs_trycatch { out.begin_block("namespace behavior"); @@ -316,7 +316,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE03_STRUCT_{}{}", out.namespace, strct.ident); + let guard = format!("CXXBRIDGE04_STRUCT_{}{}", out.namespace, strct.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { @@ -341,7 +341,7 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE03_STRUCT_{}{}", out.namespace, ety.ident); + let guard = format!("CXXBRIDGE04_STRUCT_{}{}", out.namespace, ety.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { @@ -362,7 +362,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE03_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE04_ENUM_{}{}", out.namespace, enm.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { @@ -408,7 +408,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { out.next_section(); writeln!( out, - "const char *cxxbridge03$exception(const char *, size_t);", + "const char *cxxbridge04$exception(const char *, size_t);", ); } } @@ -553,7 +553,7 @@ fn write_cxx_function_shim( writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge03$exception(catch$, throw$.len);", + " throw$.ptr = cxxbridge04$exception(catch$, throw$.len);", ); writeln!(out, " }});"); writeln!(out, " return throw$;"); @@ -1026,7 +1026,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("extern \"C\""); out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge03"); + out.begin_block("inline namespace cxxbridge04"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1040,7 +1040,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block("namespace cxxbridge03"); + out.end_block("namespace cxxbridge04"); out.end_block("namespace rust"); } @@ -1053,19 +1053,19 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE03_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE03_RUST_BOX_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE04_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE04_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge03$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge04$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge03$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge04$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); - writeln!(out, "#endif // CXXBRIDGE03_RUST_BOX_{}", instance); + writeln!(out, "#endif // CXXBRIDGE04_RUST_BOX_{}", instance); } fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { @@ -1073,34 +1073,34 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE03_RUST_VEC_{}", instance); - writeln!(out, "#define CXXBRIDGE03_RUST_VEC_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE04_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE04_RUST_VEC_{}", instance); writeln!( out, - "void cxxbridge03$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge04$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge03$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge04$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "size_t cxxbridge03$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + "size_t cxxbridge04$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge03$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + "const {} *cxxbridge04$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", inner, instance, ); writeln!( out, - "size_t cxxbridge03$rust_vec${}$stride() noexcept;", + "size_t cxxbridge04$rust_vec${}$stride() noexcept;", instance, ); - writeln!(out, "#endif // CXXBRIDGE03_RUST_VEC_{}", instance); + writeln!(out, "#endif // CXXBRIDGE04_RUST_VEC_{}", instance); } fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { @@ -1114,12 +1114,12 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " cxxbridge03$box${}$uninit(this);", instance); + writeln!(out, " cxxbridge04$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " cxxbridge03$box${}$drop(this);", instance); + writeln!(out, " cxxbridge04$box${}$drop(this);", instance); writeln!(out, "}}"); } @@ -1130,35 +1130,35 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); - writeln!(out, " cxxbridge03$rust_vec${}$new(this);", instance); + writeln!(out, " cxxbridge04$rust_vec${}$new(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!( out, - " return cxxbridge03$rust_vec${}$drop(this);", + " return cxxbridge04$rust_vec${}$drop(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); - writeln!(out, " return cxxbridge03$rust_vec${}$len(this);", instance); + writeln!(out, " return cxxbridge04$rust_vec${}$len(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); writeln!( out, - " return cxxbridge03$rust_vec${}$data(this);", + " return cxxbridge04$rust_vec${}$data(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); - writeln!(out, " return cxxbridge03$rust_vec${}$stride();", instance); + writeln!(out, " return cxxbridge04$rust_vec${}$stride();", instance); writeln!(out, "}}"); } @@ -1166,12 +1166,12 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { let ty = Type::Ident(ident.clone()); let instance = to_mangled(&out.namespace, &ty); - writeln!(out, "#ifndef CXXBRIDGE03_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE03_UNIQUE_PTR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE04_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE04_UNIQUE_PTR_{}", instance); write_unique_ptr_common(out, &ty, types); - writeln!(out, "#endif // CXXBRIDGE03_UNIQUE_PTR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE04_UNIQUE_PTR_{}", instance); } // Shared by UniquePtr and UniquePtr>. @@ -1198,7 +1198,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!( out, - "void cxxbridge03$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge04$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); @@ -1206,7 +1206,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { if can_construct_from_value { writeln!( out, - "void cxxbridge03$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge04$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( @@ -1218,28 +1218,28 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { } writeln!( out, - "void cxxbridge03$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge04$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge03$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge04$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge03$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge04$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge03$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge04$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); @@ -1251,18 +1251,18 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE03_VECTOR_{}", instance); - writeln!(out, "#define CXXBRIDGE03_VECTOR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE04_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE04_VECTOR_{}", instance); writeln!( out, - "size_t cxxbridge03$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", + "size_t cxxbridge04$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge03$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + "const {} *cxxbridge04$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); writeln!(out, " return &s[pos];"); @@ -1270,5 +1270,5 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: write_unique_ptr_common(out, vector_ty, types); - writeln!(out, "#endif // CXXBRIDGE03_VECTOR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE04_VECTOR_{}", instance); } diff --git a/include/cxx.h b/include/cxx.h index 9a9d5fa..f050248 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,12 +15,12 @@ #endif namespace rust { -inline namespace cxxbridge03 { +inline namespace cxxbridge04 { struct unsafe_bitcopy_t; -#ifndef CXXBRIDGE03_RUST_STRING -#define CXXBRIDGE03_RUST_STRING +#ifndef CXXBRIDGE04_RUST_STRING +#define CXXBRIDGE04_RUST_STRING class String final { public: String() noexcept; @@ -49,10 +49,10 @@ private: // Size and alignment statically verified by rust_string.rs. std::array repr; }; -#endif // CXXBRIDGE03_RUST_STRING +#endif // CXXBRIDGE04_RUST_STRING -#ifndef CXXBRIDGE03_RUST_STR -#define CXXBRIDGE03_RUST_STR +#ifndef CXXBRIDGE04_RUST_STR +#define CXXBRIDGE04_RUST_STR class Str final { public: Str() noexcept; @@ -86,9 +86,9 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE03_RUST_STR +#endif // CXXBRIDGE04_RUST_STR -#ifndef CXXBRIDGE03_RUST_SLICE +#ifndef CXXBRIDGE04_RUST_SLICE template class Slice final { public: @@ -117,9 +117,9 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE03_RUST_SLICE +#endif // CXXBRIDGE04_RUST_SLICE -#ifndef CXXBRIDGE03_RUST_BOX +#ifndef CXXBRIDGE04_RUST_BOX template class Box final { public: @@ -158,9 +158,9 @@ private: void drop() noexcept; T *ptr; }; -#endif // CXXBRIDGE03_RUST_BOX +#endif // CXXBRIDGE04_RUST_BOX -#ifndef CXXBRIDGE03_RUST_VEC +#ifndef CXXBRIDGE04_RUST_VEC template class Vec final { public: @@ -218,10 +218,10 @@ private: // Size and alignment statically verified by rust_vec.rs. std::array repr; }; -#endif // CXXBRIDGE03_RUST_VEC +#endif // CXXBRIDGE04_RUST_VEC -#ifndef CXXBRIDGE03_RUST_FN -#define CXXBRIDGE03_RUST_FN +#ifndef CXXBRIDGE04_RUST_FN +#define CXXBRIDGE04_RUST_FN template class Fn; @@ -238,10 +238,10 @@ private: template using TryFn = Fn; -#endif // CXXBRIDGE03_RUST_FN +#endif // CXXBRIDGE04_RUST_FN -#ifndef CXXBRIDGE03_RUST_ERROR -#define CXXBRIDGE03_RUST_ERROR +#ifndef CXXBRIDGE04_RUST_ERROR +#define CXXBRIDGE04_RUST_ERROR class Error final : std::exception { public: Error(const Error &); @@ -253,16 +253,16 @@ public: private: Str::Repr msg; }; -#endif // CXXBRIDGE03_RUST_ERROR +#endif // CXXBRIDGE04_RUST_ERROR -#ifndef CXXBRIDGE03_RUST_ISIZE -#define CXXBRIDGE03_RUST_ISIZE +#ifndef CXXBRIDGE04_RUST_ISIZE +#define CXXBRIDGE04_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif -#endif // CXXBRIDGE03_RUST_ISIZE +#endif // CXXBRIDGE04_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -296,17 +296,17 @@ Fn Fn::operator*() const noexcept { return *this; } -#ifndef CXXBRIDGE03_RUST_BITCOPY -#define CXXBRIDGE03_RUST_BITCOPY +#ifndef CXXBRIDGE04_RUST_BITCOPY +#define CXXBRIDGE04_RUST_BITCOPY struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; }; constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE03_RUST_BITCOPY +#endif // CXXBRIDGE04_RUST_BITCOPY -#ifndef CXXBRIDGE03_RUST_SLICE -#define CXXBRIDGE03_RUST_SLICE +#ifndef CXXBRIDGE04_RUST_SLICE +#define CXXBRIDGE04_RUST_SLICE template Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} @@ -344,10 +344,10 @@ template Slice::operator Repr() noexcept { return this->repr; } -#endif // CXXBRIDGE03_RUST_SLICE +#endif // CXXBRIDGE04_RUST_SLICE -#ifndef CXXBRIDGE03_RUST_BOX -#define CXXBRIDGE03_RUST_BOX +#ifndef CXXBRIDGE04_RUST_BOX +#define CXXBRIDGE04_RUST_BOX template Box::Box(const Box &other) : Box(*other) {} @@ -443,10 +443,10 @@ T *Box::into_raw() noexcept { template Box::Box() noexcept {} -#endif // CXXBRIDGE03_RUST_BOX +#endif // CXXBRIDGE04_RUST_BOX -#ifndef CXXBRIDGE03_RUST_VEC -#define CXXBRIDGE03_RUST_VEC +#ifndef CXXBRIDGE04_RUST_VEC +#define CXXBRIDGE04_RUST_VEC template Vec::Vec(Vec &&other) noexcept { this->repr = other.repr; @@ -551,7 +551,7 @@ typename Vec::const_iterator Vec::end() const noexcept { // Internal API only intended for the cxxbridge code generator. template Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} -#endif // CXXBRIDGE03_RUST_VEC +#endif // CXXBRIDGE04_RUST_VEC -} // namespace cxxbridge03 +} // namespace cxxbridge04 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index c2d88ce..2f797b0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -689,7 +689,7 @@ fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge04$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -718,7 +718,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge03$rust_vec${}{}$", namespace, elem); + let link_prefix = format!("cxxbridge04$rust_vec${}{}$", namespace, elem); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -764,7 +764,7 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { let name = ident.to_string(); - let prefix = format!("cxxbridge03$unique_ptr${}{}$", namespace, ident); + let prefix = format!("cxxbridge04$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -837,10 +837,10 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { let name = elem.to_string(); - let prefix = format!("cxxbridge03$std$vector${}{}$", namespace, elem); + let prefix = format!("cxxbridge04$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge03$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!("cxxbridge04$unique_ptr$std$vector${}{}$", namespace, elem); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/src/cxx.cc b/src/cxx.cc index 2114598..cd75162 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -7,30 +7,30 @@ #include extern "C" { -const char *cxxbridge03$cxx_string$data(const std::string &s) noexcept { +const char *cxxbridge04$cxx_string$data(const std::string &s) noexcept { return s.data(); } -size_t cxxbridge03$cxx_string$length(const std::string &s) noexcept { +size_t cxxbridge04$cxx_string$length(const std::string &s) noexcept { return s.length(); } // rust::String -void cxxbridge03$string$new(rust::String *self) noexcept; -void cxxbridge03$string$clone(rust::String *self, +void cxxbridge04$string$new(rust::String *self) noexcept; +void cxxbridge04$string$clone(rust::String *self, const rust::String &other) noexcept; -bool cxxbridge03$string$from(rust::String *self, const char *ptr, +bool cxxbridge04$string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge03$string$drop(rust::String *self) noexcept; -const char *cxxbridge03$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge03$string$len(const rust::String *self) noexcept; +void cxxbridge04$string$drop(rust::String *self) noexcept; +const char *cxxbridge04$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge04$string$len(const rust::String *self) noexcept; // rust::Str -bool cxxbridge03$str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge04$str$valid(const char *ptr, size_t len) noexcept; } // extern "C" namespace rust { -inline namespace cxxbridge03 { +inline namespace cxxbridge04 { template void panic [[noreturn]] (const char *msg) { @@ -44,42 +44,42 @@ void panic [[noreturn]] (const char *msg) { template void panic[[noreturn]] (const char *msg); -String::String() noexcept { cxxbridge03$string$new(this); } +String::String() noexcept { cxxbridge04$string$new(this); } String::String(const String &other) noexcept { - cxxbridge03$string$clone(this, other); + cxxbridge04$string$clone(this, other); } String::String(String &&other) noexcept { this->repr = other.repr; - cxxbridge03$string$new(&other); + cxxbridge04$string$new(&other); } -String::~String() noexcept { cxxbridge03$string$drop(this); } +String::~String() noexcept { cxxbridge04$string$drop(this); } String::String(const std::string &s) : String(s.data(), s.length()) {} String::String(const char *s) : String(s, std::strlen(s)) {} String::String(const char *s, size_t len) { - if (!cxxbridge03$string$from(this, s, len)) { + if (!cxxbridge04$string$from(this, s, len)) { panic("data for rust::String is not utf-8"); } } String &String::operator=(const String &other) noexcept { if (this != &other) { - cxxbridge03$string$drop(this); - cxxbridge03$string$clone(this, other); + cxxbridge04$string$drop(this); + cxxbridge04$string$clone(this, other); } return *this; } String &String::operator=(String &&other) noexcept { if (this != &other) { - cxxbridge03$string$drop(this); + cxxbridge04$string$drop(this); this->repr = other.repr; - cxxbridge03$string$new(&other); + cxxbridge04$string$new(&other); } return *this; } @@ -89,12 +89,12 @@ String::operator std::string() const { } const char *String::data() const noexcept { - return cxxbridge03$string$ptr(this); + return cxxbridge04$string$ptr(this); } -size_t String::size() const noexcept { return cxxbridge03$string$len(this); } +size_t String::size() const noexcept { return cxxbridge04$string$len(this); } -size_t String::length() const noexcept { return cxxbridge03$string$len(this); } +size_t String::length() const noexcept { return cxxbridge04$string$len(this); } String::String(unsafe_bitcopy_t, const String &bits) noexcept : repr(bits.repr) {} @@ -113,7 +113,7 @@ Str::Str(const std::string &s) : Str(s.data(), s.length()) {} Str::Str(const char *s) : Str(s, std::strlen(s)) {} Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { - if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge04$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } } @@ -143,7 +143,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { } extern "C" { -const char *cxxbridge03$error(const char *ptr, size_t len) { +const char *cxxbridge04$error(const char *ptr, size_t len) { char *copy = new char[len]; strncpy(copy, ptr, len); return copy; @@ -153,7 +153,7 @@ const char *cxxbridge03$error(const char *ptr, size_t len) { Error::Error(Str::Repr msg) noexcept : msg(msg) {} Error::Error(const Error &other) { - this->msg.ptr = cxxbridge03$error(other.msg.ptr, other.msg.len); + this->msg.ptr = cxxbridge04$error(other.msg.ptr, other.msg.len); this->msg.len = other.msg.len; } @@ -168,96 +168,96 @@ Error::~Error() noexcept { delete[] this->msg.ptr; } const char *Error::what() const noexcept { return this->msg.ptr; } -} // namespace cxxbridge03 +} // namespace cxxbridge04 } // namespace rust extern "C" { -void cxxbridge03$unique_ptr$std$string$null( +void cxxbridge04$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge03$unique_ptr$std$string$raw(std::unique_ptr *ptr, +void cxxbridge04$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); } -const std::string *cxxbridge03$unique_ptr$std$string$get( +const std::string *cxxbridge04$unique_ptr$std$string$get( const std::unique_ptr &ptr) noexcept { return ptr.get(); } -std::string *cxxbridge03$unique_ptr$std$string$release( +std::string *cxxbridge04$unique_ptr$std$string$release( std::unique_ptr &ptr) noexcept { return ptr.release(); } -void cxxbridge03$unique_ptr$std$string$drop( +void cxxbridge04$unique_ptr$std$string$drop( std::unique_ptr *ptr) noexcept { ptr->~unique_ptr(); } } // extern "C" #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ - size_t cxxbridge03$std$vector$##RUST_TYPE##$size( \ + size_t cxxbridge04$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ } \ - const CXX_TYPE *cxxbridge03$std$vector$##RUST_TYPE##$get_unchecked( \ + const CXX_TYPE *cxxbridge04$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ return &s[pos]; \ } \ - void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$null( \ + void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ } \ - void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$raw( \ std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ new (ptr) std::unique_ptr>(raw); \ } \ const std::vector \ - *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$get( \ + *cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$get( \ const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ std::vector \ - *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$release( \ + *cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$release( \ std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ - void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$drop( \ std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ } #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ - void cxxbridge03$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge04$rust_vec$##RUST_TYPE##$new( \ rust::Vec *ptr) noexcept; \ - void cxxbridge03$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge04$rust_vec$##RUST_TYPE##$drop( \ rust::Vec *ptr) noexcept; \ - size_t cxxbridge03$rust_vec$##RUST_TYPE##$len( \ + size_t cxxbridge04$rust_vec$##RUST_TYPE##$len( \ const rust::Vec *ptr) noexcept; \ - const CXX_TYPE *cxxbridge03$rust_vec$##RUST_TYPE##$data( \ + const CXX_TYPE *cxxbridge04$rust_vec$##RUST_TYPE##$data( \ const rust::Vec *ptr) noexcept; \ - size_t cxxbridge03$rust_vec$##RUST_TYPE##$stride() noexcept; + size_t cxxbridge04$rust_vec$##RUST_TYPE##$stride() noexcept; #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ Vec::Vec() noexcept { \ - cxxbridge03$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge04$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Vec::drop() noexcept { \ - return cxxbridge03$rust_vec$##RUST_TYPE##$drop(this); \ + return cxxbridge04$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ size_t Vec::size() const noexcept { \ - return cxxbridge03$rust_vec$##RUST_TYPE##$len(this); \ + return cxxbridge04$rust_vec$##RUST_TYPE##$len(this); \ } \ template <> \ const CXX_TYPE *Vec::data() const noexcept { \ - return cxxbridge03$rust_vec$##RUST_TYPE##$data(this); \ + return cxxbridge04$rust_vec$##RUST_TYPE##$data(this); \ } \ template <> \ size_t Vec::stride() noexcept { \ - return cxxbridge03$rust_vec$##RUST_TYPE##$stride(); \ + return cxxbridge04$rust_vec$##RUST_TYPE##$stride(); \ } // Usize and isize are the same type as one of the below. @@ -290,7 +290,7 @@ FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) } // extern "C" namespace rust { -inline namespace cxxbridge03 { +inline namespace cxxbridge04 { FOR_EACH_RUST_VEC(RUST_VEC_OPS) -} // namespace cxxbridge03 +} // namespace cxxbridge04 } // namespace rust diff --git a/src/cxx_string.rs b/src/cxx_string.rs index ffd0c5c..95d560e 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -4,9 +4,9 @@ use std::slice; use std::str::{self, Utf8Error}; extern "C" { - #[link_name = "cxxbridge03$cxx_string$data"] + #[link_name = "cxxbridge04$cxx_string$data"] fn string_data(_: &CxxString) -> *const u8; - #[link_name = "cxxbridge03$cxx_string$length"] + #[link_name = "cxxbridge04$cxx_string$length"] fn string_length(_: &CxxString) -> usize; } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 10853a6..a7d3f2f 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -139,7 +139,7 @@ macro_rules! impl_vector_element { fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$size")] + #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$size")] fn __vector_size(_: &CxxVector<$ty>) -> usize; } } @@ -148,7 +148,7 @@ macro_rules! impl_vector_element { unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$std$vector$", $segment, "$get_unchecked")] + #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } @@ -157,7 +157,7 @@ macro_rules! impl_vector_element { fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$null")] + #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$null")] fn __unique_ptr_null(this: *mut *mut c_void); } } @@ -168,7 +168,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$raw")] + #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$raw")] fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); } } @@ -179,7 +179,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$get")] + #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$get")] fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; } } @@ -188,7 +188,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$release")] + #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$release")] fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; } } @@ -197,7 +197,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { extern "C" { attr! { - #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", $segment, "$drop")] + #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$drop")] fn __unique_ptr_drop(this: *mut *mut c_void); } } diff --git a/src/result.rs b/src/result.rs index 0373bd8..72c4959 100644 --- a/src/result.rs +++ b/src/result.rs @@ -32,7 +32,7 @@ unsafe fn to_c_error(msg: String) -> Result { let len = msg.len(); extern "C" { - #[link_name = "cxxbridge03$error"] + #[link_name = "cxxbridge04$error"] fn error(ptr: *const u8, len: usize) -> *const u8; } diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index 849db3b..7484d1c 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -1,6 +1,6 @@ use std::slice; -#[export_name = "cxxbridge03$exception"] +#[export_name = "cxxbridge04$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { let slice = slice::from_raw_parts(ptr, len); let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index 6dc04ac..5111c6a 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -1,7 +1,7 @@ use std::slice; use std::str; -#[export_name = "cxxbridge03$str$valid"] +#[export_name = "cxxbridge04$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index d8e0f4a..94b2110 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -8,17 +8,17 @@ pub(crate) struct RustString { repr: String, } -#[export_name = "cxxbridge03$string$new"] +#[export_name = "cxxbridge04$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge03$string$clone"] +#[export_name = "cxxbridge04$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge03$string$from"] +#[export_name = "cxxbridge04$string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -34,17 +34,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge03$string$drop"] +#[export_name = "cxxbridge04$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge03$string$ptr"] +#[export_name = "cxxbridge04$string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge03$string$len"] +#[export_name = "cxxbridge04$string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 5465471..5081ec3 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -21,31 +21,31 @@ macro_rules! rust_vec_shims { const _: () = { attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$new")] + #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { ptr::write(this, RustVec { repr: Vec::new() }); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$drop")] + #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$len")] + #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { (*this).repr.len() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$data")] + #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { (*this).repr.as_ptr() } } attr! { - #[export_name = concat!("cxxbridge03$rust_vec$", $segment, "$stride")] + #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$stride")] unsafe extern "C" fn __stride() -> usize { mem::size_of::<$ty>() } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 34a1f0e..0428b5f 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -174,15 +174,15 @@ pub unsafe trait UniquePtrTarget { } extern "C" { - #[link_name = "cxxbridge03$unique_ptr$std$string$null"] + #[link_name = "cxxbridge04$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge03$unique_ptr$std$string$raw"] + #[link_name = "cxxbridge04$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); - #[link_name = "cxxbridge03$unique_ptr$std$string$get"] + #[link_name = "cxxbridge04$unique_ptr$std$string$get"] fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; - #[link_name = "cxxbridge03$unique_ptr$std$string$release"] + #[link_name = "cxxbridge04$unique_ptr$std$string$release"] fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; - #[link_name = "cxxbridge03$unique_ptr$std$string$drop"] + #[link_name = "cxxbridge04$unique_ptr$std$string$drop"] fn unique_ptr_std_string_drop(this: *mut *mut c_void); } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index c9392db..1c8c917 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -3,7 +3,7 @@ use crate::syntax::symbol::{self, Symbol}; use crate::syntax::ExternFn; use proc_macro2::Ident; -const CXXBRIDGE: &str = "cxxbridge03"; +const CXXBRIDGE: &str = "cxxbridge04"; macro_rules! join { ($($segment:expr),*) => { diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 066d238..c8500b9 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -4,7 +4,7 @@ use quote::ToTokens; use std::fmt::{self, Display, Write}; // A mangled symbol consisting of segments separated by '$'. -// For example: cxxbridge03$string$new +// For example: cxxbridge04$string$new pub struct Symbol(String); impl Display for Symbol { From 5f3fb89a2a71931ab911c0cf0dd17c72bfd60e5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 06:03:46 +0000 Subject: [PATCH 830/2232] Release 0.4.0 --- diff --git a/Cargo.toml b/Cargo.toml index 2513633..510623f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.3.9" # remember to update html_root_url +version = "0.4.0" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.3.9", path = "macro" } +cxxbridge-macro = { version = "=0.4.0", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.3.9", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.0", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.3.9", path = "gen/build" } +cxx-build = { version = "=0.4.0", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/README.md b/README.md index 9c65df6..5a4af1b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ can be 100% safe. ```toml [dependencies] -cxx = "0.3" +cxx = "0.4" ``` *Compiler support: requires rustc 1.42+ and c++11 or newer*
@@ -219,7 +219,7 @@ set up any additional source files and compiler flags as normal. # Cargo.toml [build-dependencies] -cxx-build = "0.3" +cxx-build = "0.4" ``` ```rust @@ -307,11 +307,11 @@ returns of functions. Stringrust::String &strrust::Str &[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet -CxxStringstd::stringcannot be passed by value +CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type -UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type Vec<T>rust::Vec<T>cannot hold opaque C++ type -CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type +CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far Result<T>throw/catchallowed as return type only diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 5bff819..9fefd37 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.3.9" +version = "0.4.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index eed5281..c23e82a 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.3.9" +version = "0.4.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 68ec3ec..6528cf0 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.3.9" +version = "0.4.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index b273229..be24ab3 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.3.9" +version = "0.4.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -19,7 +19,7 @@ quote = "1.0.4" syn = { version = "1.0.20", features = ["full"] } [dev-dependencies] -cxx = { version = "0.3", path = ".." } +cxx = { version = "0.4", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/lib.rs b/src/lib.rs index bbd79f3..1e76581 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! # Cargo.toml //! //! [build-dependencies] -//! cxx-build = "0.3" +//! cxx-build = "0.4" //! ``` //! //! ```no_run @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.3.9")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.0")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b335d31..f1ec3e0 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.3.9" +version = "0.4.0" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.3.9" +version = "0.4.0" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.3.9" +version = "0.4.0" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.3.9" +version = "0.4.0" [[package]] name = "cxxbridge-macro" -version = "0.3.9" +version = "0.4.0" dependencies = [ "cxx", "proc-macro2", From 1c7597933de540b1adfa07130eccf7331b0d81b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:16:28 +0000 Subject: [PATCH 831/2232] Move non-workflow-specific logic out of cxx-build root --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 1628510..4b81e1d 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -56,10 +56,11 @@ mod cargo; mod error; mod gen; +mod out; mod paths; mod syntax; -use crate::error::{Error, Result}; +use crate::error::Result; use crate::gen::error::report; use crate::gen::{fs, Opt}; use crate::paths::{PathExt, TargetDir}; @@ -138,12 +139,12 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul fn write_header(prj: &Project) { let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); - let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); if let TargetDir::Path(target_dir) = &prj.target_dir { let ref header_dir = target_dir.join("cxxbridge").join("rust"); let _ = fs::create_dir_all(header_dir); let ref cxx_h = header_dir.join("cxx.h"); - let _ = write(cxx_h, gen::include::HEADER.as_bytes()); + let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); } } @@ -171,7 +172,7 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let ref rel_path_h = rel_path.with_appended_extension(".h"); let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); - write(header_path, &generated.header)?; + out::write(header_path, &generated.header)?; let ref link_path = paths::namespaced(&prj.out_dir, rel_path); let _ = paths::symlink_or_copy(header_path, link_path); @@ -184,30 +185,7 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let ref rel_path_cc = rel_path.with_appended_extension(".cc"); let ref implementation_path = paths::namespaced(&prj.out_dir, rel_path_cc); - write(implementation_path, &generated.implementation)?; + out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); Ok(()) } - -fn write(path: &Path, content: &[u8]) -> Result<()> { - let mut create_dir_error = None; - if path.exists() { - if let Ok(existing) = fs::read(path) { - if existing == content { - // Avoid bumping modified time with unchanged contents. - return Ok(()); - } - } - let _ = fs::remove_file(path); - } else { - let parent = path.parent().unwrap(); - create_dir_error = fs::create_dir_all(parent).err(); - } - - match fs::write(path, content) { - // As long as write succeeded, ignore any create_dir_all error. - Ok(()) => Ok(()), - // If create_dir_all and write both failed, prefer the first error. - Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), - } -} diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs new file mode 100644 index 0000000..944c822 --- /dev/null +++ b/gen/build/src/out.rs @@ -0,0 +1,28 @@ +use crate::error::{Error, Result}; +use crate::gen::fs; +use std::path::Path; + +pub(crate) fn write(path: impl AsRef, content: &[u8]) -> Result<()> { + let path = path.as_ref(); + + let mut create_dir_error = None; + if path.exists() { + if let Ok(existing) = fs::read(path) { + if existing == content { + // Avoid bumping modified time with unchanged contents. + return Ok(()); + } + } + let _ = fs::remove_file(path); + } else { + let parent = path.parent().unwrap(); + create_dir_error = fs::create_dir_all(parent).err(); + } + + match fs::write(path, content) { + // As long as write succeeded, ignore any create_dir_all error. + Ok(()) => Ok(()), + // If create_dir_all and write both failed, prefer the first error. + Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), + } +} From a55c39460fac5e6b4c96c2b2414eaf2928c43809 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:19:28 +0000 Subject: [PATCH 832/2232] Fix error on creating symlink that already exists --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 4b81e1d..9498fe2 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -160,8 +160,7 @@ fn symlink_crate(prj: &Project, build: &mut Build) { let mut link = paths::include_dir(prj); link.push("CRATE"); - let _ = fs::create_dir_all(&link); - let _ = paths::symlink_dir(manifest_dir, link.join(package_name)); + let _ = out::symlink_dir(manifest_dir, link.join(package_name)); build.include(link); } @@ -175,12 +174,11 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> out::write(header_path, &generated.header)?; let ref link_path = paths::namespaced(&prj.out_dir, rel_path); - let _ = paths::symlink_or_copy(header_path, link_path); + let _ = out::symlink_file(header_path, link_path); if let TargetDir::Path(target_dir) = &prj.target_dir { let ref link_path = paths::namespaced(target_dir, rel_path); - let _ = fs::create_dir_all(link_path.parent().unwrap()); - let _ = paths::symlink_or_copy(header_path, link_path); - let _ = paths::symlink_or_copy(header_path, link_path.with_appended_extension(".h")); + let _ = out::symlink_file(header_path, link_path); + let _ = out::symlink_file(header_path, link_path.with_appended_extension(".h")); } let ref rel_path_cc = rel_path.with_appended_extension(".cc"); diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs index 944c822..76e27f6 100644 --- a/gen/build/src/out.rs +++ b/gen/build/src/out.rs @@ -1,5 +1,6 @@ use crate::error::{Error, Result}; use crate::gen::fs; +use crate::paths; use std::path::Path; pub(crate) fn write(path: impl AsRef, content: &[u8]) -> Result<()> { @@ -26,3 +27,43 @@ pub(crate) fn write(path: impl AsRef, content: &[u8]) -> Result<()> { Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), } } + +pub(crate) fn symlink_file(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + + let mut create_dir_error = None; + if dst.exists() { + let _ = fs::remove_file(dst).unwrap(); + } else { + let parent = dst.parent().unwrap(); + create_dir_error = fs::create_dir_all(parent).err(); + } + + match paths::symlink_or_copy(src, dst) { + // As long as symlink_or_copy succeeded, ignore any create_dir_all error. + Ok(()) => Ok(()), + // If create_dir_all and symlink_or_copy both failed, prefer the first error. + Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), + } +} + +pub(crate) fn symlink_dir(src: impl AsRef, dst: impl AsRef) -> Result<()> { + let src = src.as_ref(); + let dst = dst.as_ref(); + + let mut create_dir_error = None; + if dst.exists() { + let _ = fs::remove_file(dst).unwrap(); + } else { + let parent = dst.parent().unwrap(); + create_dir_error = fs::create_dir_all(parent).err(); + } + + match paths::symlink_dir(src, dst) { + // As long as symlink_dir succeeded, ignore any create_dir_all error. + Ok(()) => Ok(()), + // If create_dir_all and symlink_dir both failed, prefer the first error. + Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), + } +} From f486d56bc18a8ae58ac43a721e2992a7903117d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:26:32 +0000 Subject: [PATCH 833/2232] Consistent signature for symlink_or_copy across platforms --- diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 96e3708..d4b2ebb 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -99,7 +99,7 @@ pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { pub(crate) use self::fs::symlink_file as symlink_or_copy; #[cfg(windows)] -pub(crate) fn symlink_or_copy(src: impl AsRef, dst: impl AsRef) -> Result<()> { +pub(crate) fn symlink_or_copy(src: impl AsRef, dst: impl AsRef) -> fs::Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. let src = src.as_ref(); @@ -117,6 +117,6 @@ pub(crate) use self::fs::copy as symlink_or_copy; pub(crate) use self::fs::symlink_dir; #[cfg(not(any(unix, windows)))] -pub(crate) fn symlink_dir(_src: impl AsRef, _dst: impl AsRef) -> Result<()> { +pub(crate) fn symlink_dir(_src: impl AsRef, _dst: impl AsRef) -> fs::Result<()> { Ok(()) } diff --git a/gen/src/fs.rs b/gen/src/fs.rs index fe15f86..f135a8a 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; use std::io; use std::path::{Path, PathBuf}; -type Result = std::result::Result; +pub(crate) type Result = std::result::Result; #[derive(Debug)] pub(crate) struct Error { From e61db0e4f9d0d2f2cb8d41bae58abb0f1c628712 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:38:03 +0000 Subject: [PATCH 834/2232] Fix symlink removal on windows --- diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs index 76e27f6..b97e992 100644 --- a/gen/build/src/out.rs +++ b/gen/build/src/out.rs @@ -54,7 +54,7 @@ pub(crate) fn symlink_dir(src: impl AsRef, dst: impl AsRef) -> Resul let mut create_dir_error = None; if dst.exists() { - let _ = fs::remove_file(dst).unwrap(); + let _ = paths::remove_symlink_dir(dst).unwrap(); } else { let parent = dst.parent().unwrap(); create_dir_error = fs::create_dir_all(parent).err(); diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index d4b2ebb..1007730 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -120,3 +120,11 @@ pub(crate) use self::fs::symlink_dir; pub(crate) fn symlink_dir(_src: impl AsRef, _dst: impl AsRef) -> fs::Result<()> { Ok(()) } + +#[cfg(not(windows))] +pub(crate) use self::fs::remove_file as remove_symlink_dir; + +// On Windows, trying to use remove_file to remove a symlink which points to a +// directory fails with "Access is denied". +#[cfg(windows)] +pub(crate) use self::fs::remove_dir as remove_symlink_dir; diff --git a/gen/src/fs.rs b/gen/src/fs.rs index f135a8a..89965ee 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -74,6 +74,14 @@ pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { } } +pub(crate) fn remove_dir(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + match std::fs::remove_dir(path) { + Ok(()) => Ok(()), + Err(e) => err!(e, "Failed to remove directory `{}`", path), + } +} + fn symlink<'a>( src: &'a Path, dst: &'a Path, From 8745f7f5f8e547b9ebbc79dc9c10df232c99c239 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:46:06 +0000 Subject: [PATCH 835/2232] Release 0.4.1 --- diff --git a/Cargo.toml b/Cargo.toml index 510623f..0e69aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.0" # remember to update html_root_url +version = "0.4.1" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.0", path = "macro" } +cxxbridge-macro = { version = "=0.4.1", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.0", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.1", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.0", path = "gen/build" } +cxx-build = { version = "=0.4.1", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 9fefd37..5bab2bc 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.0" +version = "0.4.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index c23e82a..6055066 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.0" +version = "0.4.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 6528cf0..bc84b98 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.0" +version = "0.4.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index be24ab3..531dbca 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.0" +version = "0.4.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 1e76581..ef5974d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.4.0")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.1")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f1ec3e0..4f1d809 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.0" +version = "0.4.1" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.0" +version = "0.4.1" [[package]] name = "cxxbridge-macro" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cxx", "proc-macro2", From 9c815dfba2b29987b9e357990816a6380551034e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 16:54:19 +0000 Subject: [PATCH 836/2232] Rephrase build robustness note --- diff --git a/README.md b/README.md index 5a4af1b..6a7581d 100644 --- a/README.md +++ b/README.md @@ -343,10 +343,9 @@ This is still early days for CXX; I am releasing it as a minimum viable product to collect feedback on the direction and invite collaborators. Please check the open issues. -On the build side, I don't have much experience with the `cc` crate so I expect -there may be someone who can suggest ways to make that aspect of this crate -friendlier or more robust. Please report issues if you run into trouble building -or linking any of this stuff. +Especially please report issues if you run into trouble building or linking any +of this stuff. I'm sure there are ways to make the build aspects friendlier or +more robust. Finally, I know more about Rust library design than C++ library design so I would appreciate help making the C++ APIs in this project more idiomatic where From b75f59424cbf4e55174c34e4406dc75cc81c071a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 17:18:46 +0000 Subject: [PATCH 837/2232] Log the include directories On build script failure, the output looks like: running: "c++" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-m64" "-I" "/git/cxx/target/cxxbridge" "-I" "/git/cxx/target/cxxbridge/CRATE" "-Wall" "-Wextra" "-std=c++14" "-o" "/git/cxx/target/debug/build/demo-33ada9770433c2cc/out/src/demo.o" "-c" "src/demo.cc" cargo:warning=src/demo.cc:2:10: fatal error: path/to/nonexistent.rs.h: No such file or directory cargo:warning= 2 | #include "path/to/nonexistent.rs.h" cargo:warning= | ^~~~~~~~~~~~~~~~~~~~~~~~~~ cargo:warning=compilation terminated. exit code: 1 --- stderr CXX include path: /git/cxx/target/cxxbridge /git/cxx/target/cxxbridge/CRATE error occurred: Command "c++" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-m64" "-I" "/git/cxx/target/cxxbridge" "-I" "/git/cxx/target/cxxbridge/CRATE" "-Wall" "-Wextra" "-std=c++14" "-o" "/git/cxx/target/debug/build/demo-33ada9770433c2cc/out/src/demo.o" "-c" "src/demo.cc" with args "c++" did not execute successfully (status code exit code: 1). --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 9498fe2..ef284c4 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -123,17 +123,24 @@ impl Project { fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; + let include_dir = paths::include_dir(prj); + let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - build.include(paths::include_dir(prj)); + build.include(&include_dir); write_header(prj); - symlink_crate(prj, &mut build); + let crate_dir = symlink_crate(prj, &mut build); for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; } + eprintln!("\nCXX include path:"); + eprintln!(" {}", include_dir.display()); + if let Some(crate_dir) = crate_dir { + eprintln!(" {}", crate_dir.display()); + } Ok(build) } @@ -148,20 +155,21 @@ fn write_header(prj: &Project) { } } -fn symlink_crate(prj: &Project, build: &mut Build) { +fn symlink_crate(prj: &Project, build: &mut Build) -> Option { let manifest_dir = match paths::manifest_dir() { Some(manifest_dir) => manifest_dir, - None => return, + None => return None, }; let package_name = match paths::package_name() { Some(package_name) => package_name, - None => return, + None => return None, }; let mut link = paths::include_dir(prj); link.push("CRATE"); let _ = out::symlink_dir(manifest_dir, link.join(package_name)); - build.include(link); + build.include(&link); + Some(link) } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { From 2d661b8480759abfca74519ecc0fe721958c249e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 18:40:05 +0000 Subject: [PATCH 838/2232] Add mdbook skeleton --- diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml new file mode 100644 index 0000000..c82d374 --- /dev/null +++ b/.github/workflows/site.yml @@ -0,0 +1,39 @@ +name: Deploy + +on: + push: + branches: + - master + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Get mdBook + working-directory: book + run: | + export MDBOOK_VERSION="v0.4.2" + export MDBOOK_TARBALL="mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz" + export MDBOOK_URL="https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/${MDBOOK_TARBALL}" + curl -Lf "${MDBOOK_URL}" | tar -xz + + - name: Build + working-directory: book + run: | + ./mdbook build + echo "cxx.rs" > build/CNAME + + - name: Push to gh-pages + working-directory: book/build + run: | + REV=$(git rev-parse --short HEAD) + git init + git remote add upstream https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/dtolnay/cxx + git config user.name "CXX" + git config user.email "dtolnay+cxx@gmail.com" + git add -A . + git commit -qm "Website @ ${{github.repository}}@${REV}" + git push -q upstream HEAD:refs/heads/gh-pages --force diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000..690b5b8 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1,2 @@ +/build +/mdbook diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000..bc2e913 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,13 @@ +[book] +title = "CXX" +authors = ["David Tolnay"] +description = "Guide for the `cxx` crate, a safe approach to FFI between Rust and C++." + +[rust] +edition = "2018" + +[build] +build-dir = "build" + +[output.html] +git-repository-url = "https://github.com/dtolnay/cxx" diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000..f3fa924 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +[Rust ❤️ C++](about.md) diff --git a/book/src/about.md b/book/src/about.md new file mode 100644 index 0000000..6e63bf5 --- /dev/null +++ b/book/src/about.md @@ -0,0 +1 @@ +### Coming soon From 6e043630ba6051c9e6c111e04897e8ee32a07bfd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 18:49:15 +0000 Subject: [PATCH 839/2232] Rebuild site only if push touches book directory --- diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index c82d374..39a8e48 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -4,6 +4,8 @@ on: push: branches: - master + paths: + - book/** jobs: deploy: From 37f36e92a6618221a723821a71f48915cd860a44 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 18:58:17 +0000 Subject: [PATCH 840/2232] Add readme for book directory --- diff --git a/book/README.md b/book/README.md new file mode 100644 index 0000000..e4916e0 --- /dev/null +++ b/book/README.md @@ -0,0 +1,9 @@ +Published automatically to https://cxx.rs from master branch. + +To build and view locally: + +- Install [mdBook]: `cargo install mdbook`. +- Run `mdbook build` in this directory. +- Open the generated *build/index.html*. + +[mdBook]: https://github.com/rust-lang/mdBook From 99a95e66b6aa8fc8d6c7f62a30e99ddbcc542c85 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 22:15:07 +0000 Subject: [PATCH 841/2232] Fix missing unsafe_bitcopy_t definition when using Vec #277 --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 20c63a9..b31a239 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -148,6 +148,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_vec = false; let mut needs_rust_fn = false; let mut needs_rust_isize = false; + let mut needs_unsafe_bitcopy = false; for ty in types { match ty { Type::RustBox(_) => { @@ -160,6 +161,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.new = true; out.include.type_traits = true; needs_rust_vec = true; + needs_unsafe_bitcopy = true; } Type::Str(_) => { out.include.cstdint = true; @@ -187,7 +189,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } let mut needs_rust_error = false; - let mut needs_unsafe_bitcopy = false; let mut needs_manually_drop = false; let mut needs_maybe_uninit = false; let mut needs_trycatch = false; @@ -248,7 +249,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "// #include \"rust/cxx.h\""); } - if needs_rust_string || needs_rust_vec { + if needs_rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } @@ -257,11 +258,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { include::write(out, needs_rust_str, "CXXBRIDGE04_RUST_STR"); include::write(out, needs_rust_slice, "CXXBRIDGE04_RUST_SLICE"); include::write(out, needs_rust_box, "CXXBRIDGE04_RUST_BOX"); + include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE04_RUST_BITCOPY"); include::write(out, needs_rust_vec, "CXXBRIDGE04_RUST_VEC"); include::write(out, needs_rust_fn, "CXXBRIDGE04_RUST_FN"); include::write(out, needs_rust_error, "CXXBRIDGE04_RUST_ERROR"); include::write(out, needs_rust_isize, "CXXBRIDGE04_RUST_ISIZE"); - include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE04_RUST_BITCOPY"); if needs_manually_drop { out.next_section(); From 16ab1461832d5c7a2bbb3c1e3600bda9951b2f4a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 22:15:25 +0000 Subject: [PATCH 842/2232] Fix missing panic template when using Vec #277 --- diff --git a/gen/src/write.rs b/gen/src/write.rs index b31a239..4bc633f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -141,6 +141,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { } fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { + let mut needs_panic = false; let mut needs_rust_string = false; let mut needs_rust_str = false; let mut needs_rust_slice = false; @@ -160,6 +161,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.array = true; out.include.new = true; out.include.type_traits = true; + needs_panic = true; needs_rust_vec = true; needs_unsafe_bitcopy = true; } @@ -233,7 +235,8 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge04"); - if needs_rust_string + if needs_panic + || needs_rust_string || needs_rust_str || needs_rust_slice || needs_rust_box @@ -249,6 +252,8 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "// #include \"rust/cxx.h\""); } + include::write(out, needs_panic, "CXXBRIDGE04_PANIC"); + if needs_rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); diff --git a/include/cxx.h b/include/cxx.h index f050248..23e7959 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -283,8 +283,11 @@ using try_fn = TryFn; //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details +#ifndef CXXBRIDGE04_PANIC +#define CXXBRIDGE04_PANIC template void panic [[noreturn]] (const char *msg); +#endif // CXXBRIDGE04_PANIC template Ret Fn::operator()(Args... args) const noexcept(!Throws) { From c0e07dc63cb53d952a44141afb0c97de65c85f6d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 22:39:28 +0000 Subject: [PATCH 843/2232] Accept `-` to mean stdin in command line code generator --- diff --git a/gen/src/fs.rs b/gen/src/fs.rs index 89965ee..8f94f00 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -2,7 +2,7 @@ use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; +use std::io::{self, Read}; use std::path::{Path, PathBuf}; pub(crate) type Result = std::result::Result; @@ -66,6 +66,14 @@ pub(crate) fn read(path: impl AsRef) -> Result> { } } +pub(crate) fn read_stdin() -> Result> { + let mut bytes = Vec::new(); + match io::stdin().read_to_end(&mut bytes) { + Ok(_len) => Ok(bytes), + Err(e) => err!(e, "Failed to read input from stdin"), + } +} + pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { let path = path.as_ref(); match std::fs::remove_file(path) { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index f4d643d..9578fe8 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -80,7 +80,11 @@ pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { } fn read_to_string(path: &Path) -> Result { - let bytes = fs::read(path)?; + let bytes = if path == Path::new("-") { + fs::read_stdin() + } else { + fs::read(path) + }?; match String::from_utf8(bytes) { Ok(string) => Ok(string), Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())), From ecdec70e490eb9ff0b1513a2d54e15005396fd47 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 22:53:33 +0000 Subject: [PATCH 844/2232] Merge pull request #279 from dtolnay/stdin Accept `-` to mean stdin in command line code generator --- diff --git a/gen/src/fs.rs b/gen/src/fs.rs index 89965ee..8f94f00 100644 --- a/gen/src/fs.rs +++ b/gen/src/fs.rs @@ -2,7 +2,7 @@ use std::error::Error as StdError; use std::fmt::{self, Display}; -use std::io; +use std::io::{self, Read}; use std::path::{Path, PathBuf}; pub(crate) type Result = std::result::Result; @@ -66,6 +66,14 @@ pub(crate) fn read(path: impl AsRef) -> Result> { } } +pub(crate) fn read_stdin() -> Result> { + let mut bytes = Vec::new(); + match io::stdin().read_to_end(&mut bytes) { + Ok(_len) => Ok(bytes), + Err(e) => err!(e, "Failed to read input from stdin"), + } +} + pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { let path = path.as_ref(); match std::fs::remove_file(path) { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index f4d643d..9578fe8 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -80,7 +80,11 @@ pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { } fn read_to_string(path: &Path) -> Result { - let bytes = fs::read(path)?; + let bytes = if path == Path::new("-") { + fs::read_stdin() + } else { + fs::read(path) + }?; match String::from_utf8(bytes) { Ok(string) => Ok(string), Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())), From e0b6c735e8544cf1dc34e2bb6999a8af33f773b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 02 2020 22:54:12 +0000 Subject: [PATCH 845/2232] Release 0.4.2 --- diff --git a/Cargo.toml b/Cargo.toml index 0e69aac..9f23252 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.1" # remember to update html_root_url +version = "0.4.2" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.1", path = "macro" } +cxxbridge-macro = { version = "=0.4.2", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.1", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.2", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.1", path = "gen/build" } +cxx-build = { version = "=0.4.2", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 5bab2bc..6499add 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.1" +version = "0.4.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 6055066..f41ea6e 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.1" +version = "0.4.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index bc84b98..250fe6d 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.1" +version = "0.4.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 531dbca..dae4c59 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.1" +version = "0.4.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index ef5974d..8d874bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.4.1")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.2")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 4f1d809..db84aaa 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.1" +version = "0.4.2" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.1" +version = "0.4.2" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.1" +version = "0.4.2" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.1" +version = "0.4.2" [[package]] name = "cxxbridge-macro" -version = "0.4.1" +version = "0.4.2" dependencies = [ "cxx", "proc-macro2", From 91489ec3137c48e9ada88a4b7a0a51fda8308b11 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 03 2020 19:48:49 +0000 Subject: [PATCH 846/2232] Avoid printing completely empty output Running `cxxbridge` and having it exit 0 with nothing outputed is confusing and can look to the caller like a bug. --- diff --git a/gen/src/out.rs b/gen/src/out.rs index dbb1d73..d42ea74 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -57,13 +57,16 @@ impl OutFile { pub fn content(&self) -> Vec { let front = &self.front.bytes; let content = &self.content.borrow().bytes; - let len = front.len() + !front.is_empty() as usize + content.len(); + let len = front.len() + content.len() + 1; let mut out = String::with_capacity(len); out.push_str(front); - if !front.is_empty() { + if !front.is_empty() && !content.is_empty() { out.push('\n'); } out.push_str(content); + if out.is_empty() { + out.push_str("// empty\n"); + } out.into_bytes() } } From c5cd7a1325c27010e7d2934dc4dc3dda7e29f5a3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 03 2020 22:32:34 +0000 Subject: [PATCH 847/2232] Show cxx-build dependency in readme --- diff --git a/README.md b/README.md index 6a7581d..38fe682 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ can be 100% safe. ```toml [dependencies] cxx = "0.4" + +[build-dependencies] +cxx-build = "0.4" ``` *Compiler support: requires rustc 1.42+ and c++11 or newer*
From d7fef0a16265f01bcaf79bf3156190efca9cbf35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 05 2020 06:28:56 +0000 Subject: [PATCH 848/2232] Update ui tests to nightly-2020-09-05 --- diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 366a8f4..9818e44 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -5,7 +5,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t | -----^^^^^- | | | | | doesn't have a size known at compile-time - | required by this bound in `ffi::_::__AssertSized` + | required by this bound in `__AssertSized` | - = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str` + = help: within `TypeR`, the trait `Sized` is not implemented for `str` = note: required because it appears within the type `TypeR` diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index e448a22..2d8e50a 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` +error[E0271]: type mismatch resolving `::Id == (f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)` --> $DIR/wrong_type_id.rs:11:9 | 11 | type ByteRange = crate::here::StringPiece; @@ -7,7 +7,7 @@ error[E0271]: type mismatch resolving `::I ::: $WORKSPACE/src/extern_type.rs | | pub fn verify_extern_type, Id>() {} - | ------- required by this bound in `cxx::private::verify_extern_type` + | ------- required by this bound in `verify_extern_type` | - = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` - found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` + = note: expected tuple `(f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)` + found tuple `(f, o, l, l, y, (), S, t, r, i, n, g, P, i, e, c, e)` From d003628ffa199e7144da6d84025084cb7d6cde43 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 05 2020 23:15:04 +0000 Subject: [PATCH 849/2232] Don't mark trycatch since it is not from cxx.h --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 4bc633f..68d8775 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -247,7 +247,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { || needs_unsafe_bitcopy || needs_manually_drop || needs_maybe_uninit - || needs_trycatch { writeln!(out, "// #include \"rust/cxx.h\""); } From 6c5ce84f0ca9d0d6a14130455b4a646098b4ff92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 03:10:25 +0000 Subject: [PATCH 850/2232] Remove duplicated attr macro This is already present in src/macros/concat.rs. --- diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 5081ec3..163f7a9 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -7,13 +7,6 @@ pub(crate) struct RustVec { repr: Vec, } -macro_rules! attr { - (#[$name:ident = $value:expr] $($rest:tt)*) => { - #[$name = $value] - $($rest)* - }; -} - macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); From 4d0bf3312adb56b8cbec8e457d267aac68cf99a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 04:14:36 +0000 Subject: [PATCH 851/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 86e8e72..904b5c1 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -40,7 +40,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.20/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -64,7 +64,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.39/src/**"]), + srcs = glob(["vendor/syn-1.0.40/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 7ceec9b..c9f3e0c 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -45,7 +45,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.20/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -69,7 +69,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.39/src/**"]), + srcs = glob(["vendor/syn-1.0.40/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index db84aaa..532ceff 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" dependencies = [ "unicode-xid", ] @@ -258,9 +258,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" +checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" dependencies = [ "proc-macro2", "quote", From 83f71155f2a19237336d3ca03704b70d73e40fe9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 04:15:06 +0000 Subject: [PATCH 852/2232] Linearize cycle dependency between cxx crate and cxx.cc --- diff --git a/BUCK b/BUCK index 0dfa11b..5f2a65f 100644 --- a/BUCK +++ b/BUCK @@ -2,10 +2,10 @@ rust_library( name = "cxx", srcs = glob(["src/**"], exclude = ["src/symbols/**"]), visibility = ["PUBLIC"], - rustc_flags = ["--cfg", "no_export_symbols"], deps = [ ":core", ":macro", + ":symbols", ], ) @@ -37,8 +37,7 @@ cxx_library( rust_library( name = "symbols", - srcs = glob(["src/macros/**", "src/symbols/**"]), - crate_root = "src/symbols/symbols.rs", + srcs = glob(["src/symbols/**"]), ) rust_library( diff --git a/BUILD b/BUILD index 27c6931..197cef2 100644 --- a/BUILD +++ b/BUILD @@ -2,12 +2,18 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", - srcs = glob(["src/**/*.rs"]), + srcs = glob( + ["src/**/*.rs"], + exclude = ["src/symbols/**/*.rs"], + ), proc_macro_deps = [ ":cxxbridge-macro", ], visibility = ["//visibility:public"], - deps = [":core-lib"], + deps = [ + ":core-lib", + ":symbols", + ], ) rust_binary( @@ -39,6 +45,11 @@ cc_library( ) rust_library( + name = "symbols", + srcs = glob(["src/symbols/**/*.rs"]), +) + +rust_library( name = "cxxbridge-macro", srcs = glob(["macro/src/**"]), crate_type = "proc-macro", diff --git a/Cargo.toml b/Cargo.toml index 9f23252..6443475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] +cxx-symbols = { version = "=0.4.2", path = "src/symbols" } cxxbridge-macro = { version = "=0.4.2", path = "macro" } link-cplusplus = "1.0" @@ -34,7 +35,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] +members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "src/symbols", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/lib.rs b/src/lib.rs index 8d874bd..0fb7ad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -369,9 +369,8 @@ #[cfg(built_with_cargo)] extern crate link_cplusplus; - #[macro_use] -mod macros; +extern crate symbols; mod cxx_string; mod cxx_vector; @@ -387,9 +386,6 @@ mod rust_vec; mod unique_ptr; mod unwind; -#[cfg(not(no_export_symbols))] -mod symbols; - pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; diff --git a/src/macros/assert.rs b/src/macros/assert.rs deleted file mode 100644 index 738e5bb..0000000 --- a/src/macros/assert.rs +++ /dev/null @@ -1,5 +0,0 @@ -macro_rules! const_assert_eq { - ($left:expr, $right:expr $(,)?) => { - const _: [(); $left] = [(); $right]; - }; -} diff --git a/src/macros/concat.rs b/src/macros/concat.rs deleted file mode 100644 index e67e50d..0000000 --- a/src/macros/concat.rs +++ /dev/null @@ -1,6 +0,0 @@ -macro_rules! attr { - (#[$name:ident = $value:expr] $($rest:tt)*) => { - #[$name = $value] - $($rest)* - }; -} diff --git a/src/macros/mod.rs b/src/macros/mod.rs deleted file mode 100644 index d12d96b..0000000 --- a/src/macros/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[macro_use] -mod assert; -#[macro_use] -mod concat; diff --git a/src/symbols/Cargo.toml b/src/symbols/Cargo.toml new file mode 100644 index 0000000..80728d3 --- /dev/null +++ b/src/symbols/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cxx-symbols" +version = "0.4.2" +authors = ["David Tolnay "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Implementation detail of the `cxx` crate" +repository = "https://github.com/dtolnay/cxx" +documentation = "https://docs.rs/cxx" + +[lib] +name = "symbols" +path = "lib.rs" diff --git a/src/symbols/lib.rs b/src/symbols/lib.rs new file mode 100644 index 0000000..da0f223 --- /dev/null +++ b/src/symbols/lib.rs @@ -0,0 +1,9 @@ +//! *Implementation detail of the `cxx` crate.* + +#[macro_use] +mod macros; + +mod exception; +mod rust_str; +mod rust_string; +mod rust_vec; diff --git a/src/symbols/macros/assert.rs b/src/symbols/macros/assert.rs new file mode 100644 index 0000000..5d5ea9e --- /dev/null +++ b/src/symbols/macros/assert.rs @@ -0,0 +1,7 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! const_assert_eq { + ($left:expr, $right:expr $(,)?) => { + const _: [(); $left] = [(); $right]; + }; +} diff --git a/src/symbols/macros/concat.rs b/src/symbols/macros/concat.rs new file mode 100644 index 0000000..5ee77c5 --- /dev/null +++ b/src/symbols/macros/concat.rs @@ -0,0 +1,8 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} diff --git a/src/symbols/macros/mod.rs b/src/symbols/macros/mod.rs new file mode 100644 index 0000000..d12d96b --- /dev/null +++ b/src/symbols/macros/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod assert; +#[macro_use] +mod concat; diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs deleted file mode 100644 index a9d158d..0000000 --- a/src/symbols/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod exception; -mod rust_str; -mod rust_string; -mod rust_vec; diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 163f7a9..7789454 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,4 +1,4 @@ -use super::rust_string::RustString; +use crate::rust_string::RustString; use std::mem; use std::ptr; diff --git a/src/symbols/symbols.rs b/src/symbols/symbols.rs deleted file mode 100644 index 2c052ec..0000000 --- a/src/symbols/symbols.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[path = "../macros/mod.rs"] -#[macro_use] -mod macros; - -include!("mod.rs"); diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 532ceff..5c1350a 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -63,6 +63,7 @@ version = "0.4.2" dependencies = [ "cc", "cxx-build", + "cxx-symbols", "cxx-test-suite", "cxxbridge-flags", "cxxbridge-macro", @@ -95,6 +96,10 @@ dependencies = [ ] [[package]] +name = "cxx-symbols" +version = "0.4.2" + +[[package]] name = "cxx-test-suite" version = "0.0.0" dependencies = [ From 38c8764d821da198b3451cf9b5117ee0b638965a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 05:18:08 +0000 Subject: [PATCH 853/2232] Add rust::slice and rust::vec lowercase aliases --- diff --git a/include/cxx.h b/include/cxx.h index 23e7959..3744185 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -271,7 +271,11 @@ std::ostream &operator<<(std::ostream &, const Str &); using string = String; using str = Str; template +using slice = Slice; +template using box = Box; +template +using vec = Vec; using error = Error; template using fn = Fn; From dd26bd0d7990612be99967bf0685d3a2c132bdeb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:00:27 +0000 Subject: [PATCH 854/2232] Move qualified path parsing to module --- diff --git a/syntax/mod.rs b/syntax/mod.rs index e2356e1..d6db7cb 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,6 +13,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; +mod qualified; pub mod report; pub mod set; pub mod symbol; diff --git a/syntax/namespace.rs b/syntax/namespace.rs index e2dce18..bdfb845 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,8 +1,9 @@ +use crate::syntax::qualified::QualifiedName; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; use syn::parse::{Parse, ParseStream, Result}; -use syn::{Ident, Path, Token}; +use syn::{Ident, Token}; mod kw { syn::custom_keyword!(namespace); @@ -31,10 +32,7 @@ impl Parse for Namespace { if !input.is_empty() { input.parse::()?; input.parse::()?; - let path = input.call(Path::parse_mod_style)?; - for segment in path.segments { - segments.push(segment.ident); - } + segments = input.call(QualifiedName::parse_unquoted)?.segments; input.parse::>()?; } Ok(Namespace { segments }) diff --git a/syntax/qualified.rs b/syntax/qualified.rs new file mode 100644 index 0000000..c876fa8 --- /dev/null +++ b/syntax/qualified.rs @@ -0,0 +1,17 @@ +use syn::parse::{ParseStream, Result}; +use syn::{Ident, Path}; + +pub struct QualifiedName { + pub segments: Vec, +} + +impl QualifiedName { + pub fn parse_unquoted(input: ParseStream) -> Result { + let path = input.call(Path::parse_mod_style)?; + let mut segments = Vec::with_capacity(path.segments.len()); + for segment in path.segments { + segments.push(segment.ident); + } + Ok(QualifiedName { segments }) + } +} From 6c9a622708a877257c030c679452b716c6a7a1e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:08:57 +0000 Subject: [PATCH 855/2232] Permit rust keywords in C++ qualified paths --- diff --git a/syntax/qualified.rs b/syntax/qualified.rs index c876fa8..a340093 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -1,5 +1,6 @@ +use syn::ext::IdentExt; use syn::parse::{ParseStream, Result}; -use syn::{Ident, Path}; +use syn::{Ident, Token}; pub struct QualifiedName { pub segments: Vec, @@ -7,10 +8,18 @@ pub struct QualifiedName { impl QualifiedName { pub fn parse_unquoted(input: ParseStream) -> Result { - let path = input.call(Path::parse_mod_style)?; - let mut segments = Vec::with_capacity(path.segments.len()); - for segment in path.segments { - segments.push(segment.ident); + let mut segments = Vec::new(); + let mut trailing_punct = true; + while trailing_punct && input.peek(Ident::peek_any) { + let ident = Ident::parse_any(input)?; + segments.push(ident); + let colons: Option = input.parse()?; + trailing_punct = colons.is_some(); + } + if segments.is_empty() { + return Err(input.error("expected path")); + } else if trailing_punct { + return Err(input.error("expected path segment")); } Ok(QualifiedName { segments }) } From 6b65a58f567eff5c77a17be2f1f4ea0af7ef4d33 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:21:48 +0000 Subject: [PATCH 856/2232] Merge pull request #287 from dtolnay/qual Permit rust keywords in C++ qualified paths --- diff --git a/syntax/mod.rs b/syntax/mod.rs index e2356e1..d6db7cb 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,6 +13,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; +mod qualified; pub mod report; pub mod set; pub mod symbol; diff --git a/syntax/namespace.rs b/syntax/namespace.rs index e2dce18..bdfb845 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,8 +1,9 @@ +use crate::syntax::qualified::QualifiedName; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; use syn::parse::{Parse, ParseStream, Result}; -use syn::{Ident, Path, Token}; +use syn::{Ident, Token}; mod kw { syn::custom_keyword!(namespace); @@ -31,10 +32,7 @@ impl Parse for Namespace { if !input.is_empty() { input.parse::()?; input.parse::()?; - let path = input.call(Path::parse_mod_style)?; - for segment in path.segments { - segments.push(segment.ident); - } + segments = input.call(QualifiedName::parse_unquoted)?.segments; input.parse::>()?; } Ok(Namespace { segments }) diff --git a/syntax/qualified.rs b/syntax/qualified.rs new file mode 100644 index 0000000..a340093 --- /dev/null +++ b/syntax/qualified.rs @@ -0,0 +1,26 @@ +use syn::ext::IdentExt; +use syn::parse::{ParseStream, Result}; +use syn::{Ident, Token}; + +pub struct QualifiedName { + pub segments: Vec, +} + +impl QualifiedName { + pub fn parse_unquoted(input: ParseStream) -> Result { + let mut segments = Vec::new(); + let mut trailing_punct = true; + while trailing_punct && input.peek(Ident::peek_any) { + let ident = Ident::parse_any(input)?; + segments.push(ident); + let colons: Option = input.parse()?; + trailing_punct = colons.is_some(); + } + if segments.is_empty() { + return Err(input.error("expected path")); + } else if trailing_punct { + return Err(input.error("expected path segment")); + } + Ok(QualifiedName { segments }) + } +} From bef9e6d783773b0435eae070cff4d94bb9b2e8d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:21:55 +0000 Subject: [PATCH 857/2232] Allow namespace to be given as a quoted string --- diff --git a/syntax/namespace.rs b/syntax/namespace.rs index bdfb845..49b31d1 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -32,7 +32,9 @@ impl Parse for Namespace { if !input.is_empty() { input.parse::()?; input.parse::()?; - segments = input.call(QualifiedName::parse_unquoted)?.segments; + segments = input + .call(QualifiedName::parse_quoted_or_unquoted)? + .segments; input.parse::>()?; } Ok(Namespace { segments }) diff --git a/syntax/qualified.rs b/syntax/qualified.rs index a340093..be9bceb 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -1,6 +1,6 @@ use syn::ext::IdentExt; use syn::parse::{ParseStream, Result}; -use syn::{Ident, Token}; +use syn::{Ident, LitStr, Token}; pub struct QualifiedName { pub segments: Vec, @@ -23,4 +23,13 @@ impl QualifiedName { } Ok(QualifiedName { segments }) } + + pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result { + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + lit.parse_with(Self::parse_unquoted) + } else { + Self::parse_unquoted(input) + } + } } From 3b4efdc62a614f22536532e63dd67eb6a6d8fd23 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:30:13 +0000 Subject: [PATCH 858/2232] Merge pull request #288 from dtolnay/quoted Allow namespace to be given as a quoted string --- diff --git a/syntax/namespace.rs b/syntax/namespace.rs index bdfb845..49b31d1 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -32,7 +32,9 @@ impl Parse for Namespace { if !input.is_empty() { input.parse::()?; input.parse::()?; - segments = input.call(QualifiedName::parse_unquoted)?.segments; + segments = input + .call(QualifiedName::parse_quoted_or_unquoted)? + .segments; input.parse::>()?; } Ok(Namespace { segments }) diff --git a/syntax/qualified.rs b/syntax/qualified.rs index a340093..be9bceb 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -1,6 +1,6 @@ use syn::ext::IdentExt; use syn::parse::{ParseStream, Result}; -use syn::{Ident, Token}; +use syn::{Ident, LitStr, Token}; pub struct QualifiedName { pub segments: Vec, @@ -23,4 +23,13 @@ impl QualifiedName { } Ok(QualifiedName { segments }) } + + pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result { + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + lit.parse_with(Self::parse_unquoted) + } else { + Self::parse_unquoted(input) + } + } } From a8d94a110f467721606d5473a10fd9fdbe5c0054 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:30:24 +0000 Subject: [PATCH 859/2232] Accept unquoted path in type_id macro --- diff --git a/macro/src/lib.rs b/macro/src/lib.rs index c2ad38b..fae874a 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -15,8 +15,10 @@ mod type_id; use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; +use crate::syntax::qualified::QualifiedName; use proc_macro::TokenStream; -use syn::{parse_macro_input, LitStr}; +use syn::parse::{Parse, ParseStream, Result}; +use syn::parse_macro_input; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -50,6 +52,14 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro] pub fn type_id(input: TokenStream) -> TokenStream { - let arg = parse_macro_input!(input as LitStr); - type_id::expand(arg).into() + struct TypeId(QualifiedName); + + impl Parse for TypeId { + fn parse(input: ParseStream) -> Result { + QualifiedName::parse_quoted_or_unquoted(input).map(TypeId) + } + } + + let arg = parse_macro_input!(input as TypeId); + type_id::expand(arg.0).into() } diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs index 445da2b..5c5d9cc 100644 --- a/macro/src/type_id.rs +++ b/macro/src/type_id.rs @@ -1,16 +1,16 @@ +use crate::syntax::qualified::QualifiedName; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::LitStr; // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` -pub fn expand(arg: LitStr) -> TokenStream { +pub fn expand(arg: QualifiedName) -> TokenStream { let mut ids = Vec::new(); - for word in arg.value().split("::") { + for word in arg.segments { if !ids.is_empty() { ids.push(quote!(())); } - for ch in word.chars() { + for ch in word.to_string().chars() { ids.push(match ch { 'A'..='Z' | 'a'..='z' => { let t = format_ident!("{}", ch); diff --git a/syntax/mod.rs b/syntax/mod.rs index d6db7cb..03422d1 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,7 +13,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; -mod qualified; +pub mod qualified; pub mod report; pub mod set; pub mod symbol; From c4f0ad9b5eed8bb9d9ca3f2887e0a1fd75c5cc67 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:38:26 +0000 Subject: [PATCH 860/2232] Merge pull request #289 from dtolnay/unquoted Accept unquoted path in type_id macro --- diff --git a/macro/src/lib.rs b/macro/src/lib.rs index c2ad38b..fae874a 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -15,8 +15,10 @@ mod type_id; use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; +use crate::syntax::qualified::QualifiedName; use proc_macro::TokenStream; -use syn::{parse_macro_input, LitStr}; +use syn::parse::{Parse, ParseStream, Result}; +use syn::parse_macro_input; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -50,6 +52,14 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro] pub fn type_id(input: TokenStream) -> TokenStream { - let arg = parse_macro_input!(input as LitStr); - type_id::expand(arg).into() + struct TypeId(QualifiedName); + + impl Parse for TypeId { + fn parse(input: ParseStream) -> Result { + QualifiedName::parse_quoted_or_unquoted(input).map(TypeId) + } + } + + let arg = parse_macro_input!(input as TypeId); + type_id::expand(arg.0).into() } diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs index 445da2b..5c5d9cc 100644 --- a/macro/src/type_id.rs +++ b/macro/src/type_id.rs @@ -1,16 +1,16 @@ +use crate::syntax::qualified::QualifiedName; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::LitStr; // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` -pub fn expand(arg: LitStr) -> TokenStream { +pub fn expand(arg: QualifiedName) -> TokenStream { let mut ids = Vec::new(); - for word in arg.value().split("::") { + for word in arg.segments { if !ids.is_empty() { ids.push(quote!(())); } - for ch in word.chars() { + for ch in word.to_string().chars() { ids.push(match ch { 'A'..='Z' | 'a'..='z' => { let t = format_ident!("{}", ch); diff --git a/syntax/mod.rs b/syntax/mod.rs index d6db7cb..03422d1 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -13,7 +13,7 @@ mod impls; pub mod mangle; pub mod namespace; mod parse; -mod qualified; +pub mod qualified; pub mod report; pub mod set; pub mod symbol; From bdb576ca61c53c859a7fbd0892bba877533b2ec1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:45:55 +0000 Subject: [PATCH 861/2232] Parse unsafety on function signatures --- diff --git a/syntax/impls.rs b/syntax/impls.rs index c34e3e5..ebebb3e 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -149,6 +149,7 @@ impl Eq for Signature {} impl PartialEq for Signature { fn eq(&self, other: &Signature) -> bool { let Signature { + unsafety, fn_token: _, receiver, args, @@ -158,6 +159,7 @@ impl PartialEq for Signature { throws_tokens: _, } = self; let Signature { + unsafety: unsafety2, fn_token: _, receiver: receiver2, args: args2, @@ -166,7 +168,8 @@ impl PartialEq for Signature { paren_token: _, throws_tokens: _, } = other; - receiver == receiver2 + unsafety.is_some() == unsafety2.is_some() + && receiver == receiver2 && ret == ret2 && throws == throws2 && args.len() == args2.len() @@ -177,6 +180,7 @@ impl PartialEq for Signature { impl Hash for Signature { fn hash(&self, state: &mut H) { let Signature { + unsafety, fn_token: _, receiver, args, @@ -185,6 +189,7 @@ impl Hash for Signature { paren_token: _, throws_tokens: _, } = self; + unsafety.is_some().hash(state); receiver.hash(state); for arg in args { arg.hash(state); diff --git a/syntax/mod.rs b/syntax/mod.rs index 03422d1..f52a582 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -87,6 +87,7 @@ pub struct TypeAlias { } pub struct Signature { + pub unsafety: Option, pub fn_token: Token![fn], pub receiver: Option, pub args: Punctuated, diff --git a/syntax/parse.rs b/syntax/parse.rs index 415b802..3ac5304 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -353,6 +353,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); let doc = attrs::parse_doc(cx, &foreign_fn.attrs); + let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = foreign_fn.sig.ident.clone(); let paren_token = foreign_fn.sig.paren_token; @@ -367,6 +368,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R doc, ident, sig: Signature { + unsafety, fn_token, receiver, args, @@ -578,6 +580,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { let ret = parse_return_type(&ty.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { + unsafety: ty.unsafety, fn_token: ty.fn_token, receiver: None, args, From e67bcf59fc6e1698ff7625bfda273d2e2b5ab00a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 06:50:44 +0000 Subject: [PATCH 862/2232] Expand unsafety on extern C++ functions --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 2f797b0..879431e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -399,17 +399,19 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } .unwrap_or(call); + let mut dispatch = quote!(#setup #expr); + let unsafety = &efn.sig.unsafety; + if unsafety.is_none() { + dispatch = quote!(unsafe { #dispatch }); + } let function_shim = quote! { #doc - pub fn #ident(#(#all_args,)*) #ret { + pub #unsafety fn #ident(#(#all_args,)*) #ret { extern "C" { #decl } #trampolines - unsafe { - #setup - #expr - } + #dispatch } }; match &efn.receiver { From 430b5de1ff6b4cc010823ae7a98535d7d241e8fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 07 2020 07:00:23 +0000 Subject: [PATCH 863/2232] Release 0.4.3 --- diff --git a/Cargo.toml b/Cargo.toml index 6443475..a2aaf8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.2" # remember to update html_root_url +version = "0.4.3" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,16 +20,16 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxx-symbols = { version = "=0.4.2", path = "src/symbols" } -cxxbridge-macro = { version = "=0.4.2", path = "macro" } +cxx-symbols = { version = "=0.4.3", path = "src/symbols" } +cxxbridge-macro = { version = "=0.4.3", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.2", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.3", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.2", path = "gen/build" } +cxx-build = { version = "=0.4.3", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 6499add..afae446 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.2" +version = "0.4.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index f41ea6e..acd2c3a 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.2" +version = "0.4.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 250fe6d..e0264a6 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.2" +version = "0.4.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index dae4c59..9086da6 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.2" +version = "0.4.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 0fb7ad9..8c7344b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.4.2")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.3")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/src/symbols/Cargo.toml b/src/symbols/Cargo.toml index 80728d3..93283ad 100644 --- a/src/symbols/Cargo.toml +++ b/src/symbols/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-symbols" -version = "0.4.2" +version = "0.4.3" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 5c1350a..92ac6b3 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.2" +version = "0.4.3" dependencies = [ "cc", "cxx-build", @@ -74,7 +74,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.2" +version = "0.4.3" dependencies = [ "cc", "codespan-reporting", @@ -97,7 +97,7 @@ dependencies = [ [[package]] name = "cxx-symbols" -version = "0.4.2" +version = "0.4.3" [[package]] name = "cxx-test-suite" @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.2" +version = "0.4.3" dependencies = [ "clap", "codespan-reporting", @@ -121,11 +121,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.2" +version = "0.4.3" [[package]] name = "cxxbridge-macro" -version = "0.4.2" +version = "0.4.3" dependencies = [ "cxx", "proc-macro2", From 7aa5e21ab799f689de0a94fd35a03e90e28d6745 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 08 2020 05:17:56 +0000 Subject: [PATCH 864/2232] Use lld for the buck link --- diff --git a/.buckconfig b/.buckconfig index 6a4626f..3e5092d 100644 --- a/.buckconfig +++ b/.buckconfig @@ -14,4 +14,7 @@ [rust] default_edition = 2018 - rustc_flags = -Crelocation-model=dynamic-no-pic --cap-lints=allow + rustc_flags = \ + -Clink-arg=-fuse-ld=lld \ + -Crelocation-model=dynamic-no-pic \ + --cap-lints=allow diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 315bac2..b0b9d16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,8 @@ jobs: wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/v2019.10.17.01/buck-v2019.10.17.01.pex chmod +x bin/buck echo ::add-path::bin + - name: Install lld + run: sudo apt install lld - name: Vendor dependencies run: | cp third-party/Cargo.lock . From 9f6c075e3ccadb38ed73b416c8eaf1122e16afde Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 08 2020 05:31:03 +0000 Subject: [PATCH 865/2232] Inline cxx-symbols crate into cxx The separation is no longer needed for Buck when linking with lld. --- diff --git a/BUCK b/BUCK index 5f2a65f..695c079 100644 --- a/BUCK +++ b/BUCK @@ -1,11 +1,10 @@ rust_library( name = "cxx", - srcs = glob(["src/**"], exclude = ["src/symbols/**"]), + srcs = glob(["src/**"]), visibility = ["PUBLIC"], deps = [ ":core", ":macro", - ":symbols", ], ) @@ -32,12 +31,6 @@ cxx_library( "cxx.h": "include/cxx.h", }, exported_linker_flags = ["-lstdc++"], - deps = [":symbols"], -) - -rust_library( - name = "symbols", - srcs = glob(["src/symbols/**"]), ) rust_library( diff --git a/BUILD b/BUILD index 197cef2..27c6931 100644 --- a/BUILD +++ b/BUILD @@ -2,18 +2,12 @@ load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", - srcs = glob( - ["src/**/*.rs"], - exclude = ["src/symbols/**/*.rs"], - ), + srcs = glob(["src/**/*.rs"]), proc_macro_deps = [ ":cxxbridge-macro", ], visibility = ["//visibility:public"], - deps = [ - ":core-lib", - ":symbols", - ], + deps = [":core-lib"], ) rust_binary( @@ -45,11 +39,6 @@ cc_library( ) rust_library( - name = "symbols", - srcs = glob(["src/symbols/**/*.rs"]), -) - -rust_library( name = "cxxbridge-macro", srcs = glob(["macro/src/**"]), crate_type = "proc-macro", diff --git a/Cargo.toml b/Cargo.toml index a2aaf8a..73539f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxx-symbols = { version = "=0.4.3", path = "src/symbols" } cxxbridge-macro = { version = "=0.4.3", path = "macro" } link-cplusplus = "1.0" @@ -35,7 +34,7 @@ rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } [workspace] -members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "src/symbols", "tests/ffi"] +members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/lib.rs b/src/lib.rs index 8c7344b..b0fe00d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -369,8 +369,9 @@ #[cfg(built_with_cargo)] extern crate link_cplusplus; + #[macro_use] -extern crate symbols; +mod macros; mod cxx_string; mod cxx_vector; @@ -383,6 +384,7 @@ mod rust_sliceu8; mod rust_str; mod rust_string; mod rust_vec; +mod symbols; mod unique_ptr; mod unwind; diff --git a/src/macros/assert.rs b/src/macros/assert.rs new file mode 100644 index 0000000..5d5ea9e --- /dev/null +++ b/src/macros/assert.rs @@ -0,0 +1,7 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! const_assert_eq { + ($left:expr, $right:expr $(,)?) => { + const _: [(); $left] = [(); $right]; + }; +} diff --git a/src/macros/concat.rs b/src/macros/concat.rs new file mode 100644 index 0000000..5ee77c5 --- /dev/null +++ b/src/macros/concat.rs @@ -0,0 +1,8 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! attr { + (#[$name:ident = $value:expr] $($rest:tt)*) => { + #[$name = $value] + $($rest)* + }; +} diff --git a/src/macros/mod.rs b/src/macros/mod.rs new file mode 100644 index 0000000..d12d96b --- /dev/null +++ b/src/macros/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod assert; +#[macro_use] +mod concat; diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 5e7082a..6f1d567 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -3,7 +3,7 @@ use std::mem::ManuallyDrop; #[repr(C)] pub struct RustVec { - repr: Vec, + pub(crate) repr: Vec, } impl RustVec { diff --git a/src/symbols/Cargo.toml b/src/symbols/Cargo.toml deleted file mode 100644 index 93283ad..0000000 --- a/src/symbols/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cxx-symbols" -version = "0.4.3" -authors = ["David Tolnay "] -edition = "2018" -license = "MIT OR Apache-2.0" -description = "Implementation detail of the `cxx` crate" -repository = "https://github.com/dtolnay/cxx" -documentation = "https://docs.rs/cxx" - -[lib] -name = "symbols" -path = "lib.rs" diff --git a/src/symbols/lib.rs b/src/symbols/lib.rs deleted file mode 100644 index da0f223..0000000 --- a/src/symbols/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! *Implementation detail of the `cxx` crate.* - -#[macro_use] -mod macros; - -mod exception; -mod rust_str; -mod rust_string; -mod rust_vec; diff --git a/src/symbols/macros/assert.rs b/src/symbols/macros/assert.rs deleted file mode 100644 index 5d5ea9e..0000000 --- a/src/symbols/macros/assert.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[macro_export] -#[doc(hidden)] -macro_rules! const_assert_eq { - ($left:expr, $right:expr $(,)?) => { - const _: [(); $left] = [(); $right]; - }; -} diff --git a/src/symbols/macros/concat.rs b/src/symbols/macros/concat.rs deleted file mode 100644 index 5ee77c5..0000000 --- a/src/symbols/macros/concat.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[macro_export] -#[doc(hidden)] -macro_rules! attr { - (#[$name:ident = $value:expr] $($rest:tt)*) => { - #[$name = $value] - $($rest)* - }; -} diff --git a/src/symbols/macros/mod.rs b/src/symbols/macros/mod.rs deleted file mode 100644 index d12d96b..0000000 --- a/src/symbols/macros/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[macro_use] -mod assert; -#[macro_use] -mod concat; diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs new file mode 100644 index 0000000..a9d158d --- /dev/null +++ b/src/symbols/mod.rs @@ -0,0 +1,4 @@ +mod exception; +mod rust_str; +mod rust_string; +mod rust_vec; diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 94b2110..5c2982d 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -3,11 +3,6 @@ use std::ptr; use std::slice; use std::str; -#[repr(C)] -pub(crate) struct RustString { - repr: String, -} - #[export_name = "cxxbridge04$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 7789454..fb49b40 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,12 +1,8 @@ use crate::rust_string::RustString; +use crate::rust_vec::RustVec; use std::mem; use std::ptr; -#[repr(C)] -pub(crate) struct RustVec { - repr: Vec, -} - macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::>()); diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 92ac6b3..8a2ee25 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -63,7 +63,6 @@ version = "0.4.3" dependencies = [ "cc", "cxx-build", - "cxx-symbols", "cxx-test-suite", "cxxbridge-flags", "cxxbridge-macro", @@ -96,10 +95,6 @@ dependencies = [ ] [[package]] -name = "cxx-symbols" -version = "0.4.3" - -[[package]] name = "cxx-test-suite" version = "0.0.0" dependencies = [ From e4fa873f5f034341b400091cd04132990fbd6738 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 08 2020 22:04:56 +0000 Subject: [PATCH 866/2232] Allow rust::Error to be caught as std::exception& --- diff --git a/include/cxx.h b/include/cxx.h index 3744185..3995924 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -242,7 +242,7 @@ using TryFn = Fn; #ifndef CXXBRIDGE04_RUST_ERROR #define CXXBRIDGE04_RUST_ERROR -class Error final : std::exception { +class Error final : public std::exception { public: Error(const Error &); Error(Error &&) noexcept; From c4ff07b964844bc081d3b630a15fcb3fcaa519d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 08 2020 22:11:17 +0000 Subject: [PATCH 867/2232] Release 0.4.4 --- diff --git a/Cargo.toml b/Cargo.toml index 73539f7..6613014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.3" # remember to update html_root_url +version = "0.4.4" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.3", path = "macro" } +cxxbridge-macro = { version = "=0.4.4", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.3", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.4", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.3", path = "gen/build" } +cxx-build = { version = "=0.4.4", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index afae446..fa4b95f 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.3" +version = "0.4.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index acd2c3a..ce9b206 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.3" +version = "0.4.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index e0264a6..376c9cd 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.3" +version = "0.4.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 9086da6..e5a52e4 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.3" +version = "0.4.4" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index b0fe00d..8c828a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx -#![doc(html_root_url = "https://docs.rs/cxx/0.4.3")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.4")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 8a2ee25..1310d03 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.3" +version = "0.4.4" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.3" +version = "0.4.4" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.3" +version = "0.4.4" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.3" +version = "0.4.4" [[package]] name = "cxxbridge-macro" -version = "0.4.3" +version = "0.4.4" dependencies = [ "cxx", "proc-macro2", From c8870b82954fd5535fbecf4cde6a0f279fd25eae Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 09 2020 15:44:39 +0000 Subject: [PATCH 868/2232] Add missing dependencies of Error --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 68d8775..8cc1b1b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -214,6 +214,8 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { Api::RustFunction(efn) if !out.header => { if efn.throws { out.include.exception = true; + out.include.string = true; + needs_rust_str = true; needs_rust_error = true; } for arg in &efn.args { From 2599f88134d351da6d95ae82316f0436313ac193 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 09 2020 19:51:40 +0000 Subject: [PATCH 869/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 904b5c1..1dd5a24 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -40,7 +40,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.20/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.21/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", diff --git a/third-party/BUILD b/third-party/BUILD index c9f3e0c..a95bbcb 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -45,7 +45,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.20/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.21/src/**"]), crate_features = [ "proc-macro", "span-locations", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1310d03..1c9da28 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] From 32574467aadde0eebd01c9a52e592694c111e2be Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 11 2020 02:35:03 +0000 Subject: [PATCH 870/2232] Ignore target and Cargo.lock in any directory --- diff --git a/.gitignore b/.gitignore index e7499e5..b036b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /bazel-out /bazel-testlogs /buck-out -/Cargo.lock /expand.cc /expand.rs -/target +Cargo.lock +target From cc7ced4268283e5b16c322effc9aca5c9b9372fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 11 2020 17:39:55 +0000 Subject: [PATCH 871/2232] Detect when symlink not enabled --- diff --git a/gen/build/build.rs b/gen/build/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/build/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/gen/cmd/build.rs b/gen/cmd/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/cmd/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/gen/lib/build.rs b/gen/lib/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/lib/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/macro/build.rs b/macro/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/macro/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} From d4b1ceeb01a6853c860c6f1601d7a5fdd7f57e82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 11 2020 17:45:50 +0000 Subject: [PATCH 872/2232] Build script not needed for published crate --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index ce9b206..0eb9874 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a Cargo build." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 376c9cd..2c548e6 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 48a4b72..db6de83 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into higher level tools." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/macro/Cargo.toml b/macro/Cargo.toml index e5a52e4..301a750 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "Implementation detail of the `cxx` crate." repository = "https://github.com/dtolnay/cxx" -exclude = ["README.md"] +exclude = ["build.rs", "README.md"] keywords = ["ffi"] categories = ["development-tools::ffi"] From c6f5d77bba3f76438a43ed1a19cf27266de962c7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 11 2020 17:57:03 +0000 Subject: [PATCH 873/2232] Merge pull request #295 from dtolnay/detect Detect when symlink not enabled --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index ce9b206..0eb9874 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a Cargo build." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/gen/build/build.rs b/gen/build/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/build/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 376c9cd..2c548e6 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/gen/cmd/build.rs b/gen/cmd/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/cmd/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 48a4b72..db6de83 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "C++ code generator for integrating `cxx` crate into higher level tools." repository = "https://github.com/dtolnay/cxx" +exclude = ["build.rs"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/gen/lib/build.rs b/gen/lib/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/gen/lib/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} diff --git a/macro/Cargo.toml b/macro/Cargo.toml index e5a52e4..301a750 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "MIT OR Apache-2.0" description = "Implementation detail of the `cxx` crate." repository = "https://github.com/dtolnay/cxx" -exclude = ["README.md"] +exclude = ["build.rs", "README.md"] keywords = ["ffi"] categories = ["development-tools::ffi"] diff --git a/macro/build.rs b/macro/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/macro/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} From e32c5027e93f57beeb3c260592e2684cadeeca02 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 03:03:05 +0000 Subject: [PATCH 874/2232] Show header name in help text same as it appears in #include --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 1092dcd..dce7a5a 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -13,7 +13,7 @@ type Arg = clap::Arg<'static, 'static>; const USAGE: &str = "\ cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout\ + cxxbridge --header Emit \"rust/cxx.h\" header to stdout\ "; const TEMPLATE: &str = "\ diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index f119189..110e319 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -6,7 +6,7 @@ https://github.com/dtolnay/cxx USAGE: cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout - cxxbridge --header Emit rust/cxx.h header to stdout + cxxbridge --header Emit \"rust/cxx.h\" header to stdout ARGS: From 9ef74ce98f2bb1f26bf2f7c76458832fdb8eab57 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 03:57:43 +0000 Subject: [PATCH 875/2232] Add -o flag to set output file for cli --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index dce7a5a..caab254 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -2,7 +2,7 @@ #[path = "test.rs"] mod test; -use super::Opt; +use super::{Opt, Output}; use clap::AppSettings; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -39,6 +39,7 @@ fn app() -> App { .arg(arg_cxx_impl_annotations()) .arg(arg_header()) .arg(arg_include()) + .arg(arg_output()) .help_message("Print help information.") .version_message("Print version information."); if let Some(version) = option_env!("CARGO_PKG_VERSION") { @@ -51,6 +52,7 @@ const INPUT: &str = "input"; const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; const HEADER: &str = "header"; const INCLUDE: &str = "include"; +const OUTPUT: &str = "output"; pub(super) fn from_args() -> Opt { let matches = app().get_matches(); @@ -61,6 +63,11 @@ pub(super) fn from_args() -> Opt { include: matches .values_of(INCLUDE) .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()), + output: match matches.value_of_os(OUTPUT) { + None => Output::Stdout, + Some(path) if path == "-" => Output::Stdout, + Some(path) => Output::File(PathBuf::from(path)), + }, } } @@ -114,3 +121,16 @@ into the generated C++ code as #include lines. .validator_os(validate_utf8) .help(HELP) } + +fn arg_output() -> Arg { + const HELP: &str = "\ +Path of file to write as output. Output goes to stdout if -o is +not specified. + "; + Arg::with_name(OUTPUT) + .long(OUTPUT) + .short("o") + .takes_value(true) + .validator_os(validate_utf8) + .help(HELP) +} diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 14b75e6..834b738 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -11,9 +11,11 @@ mod app; mod gen; mod syntax; -use gen::include; +use gen::error::{report, Result}; +use gen::{fs, include}; use std::io::{self, Write}; use std::path::PathBuf; +use std::process; #[derive(Debug)] struct Opt { @@ -21,13 +23,23 @@ struct Opt { header: bool, cxx_impl_annotations: Option, include: Vec, + output: Output, } -fn write(content: impl AsRef<[u8]>) { - let _ = io::stdout().lock().write_all(content.as_ref()); +#[derive(Debug)] +enum Output { + Stdout, + File(PathBuf), } fn main() { + if let Err(err) = try_main() { + let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err)); + process::exit(1); + } +} + +fn try_main() -> Result<()> { let opt = app::from_args(); let gen = gen::Opt { @@ -37,10 +49,24 @@ fn main() { gen_implementation: !opt.header, }; - match (opt.input, opt.header) { - (Some(input), true) => write(gen::generate_from_path(&input, &gen).header), - (Some(input), false) => write(gen::generate_from_path(&input, &gen).implementation), - (None, true) => write(include::HEADER), + let content; + let content = match (opt.input, opt.header) { + (Some(input), true) => { + content = gen::generate_from_path(&input, &gen).header; + content.as_slice() + } + (Some(input), false) => { + content = gen::generate_from_path(&input, &gen).implementation; + content.as_slice() + } + (None, true) => include::HEADER.as_bytes(), (None, false) => unreachable!(), // enforced by required_unless + }; + + match opt.output { + Output::Stdout => drop(io::stdout().write_all(content)), + Output::File(path) => fs::write(path, content)?, } + + Ok(()) } diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 110e319..76101d9 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -31,6 +31,10 @@ OPTIONS: parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. \x20 + -o, --output + Path of file to write as output. Output goes to stdout if -o is + not specified. + \x20 -V, --version Print version information. "; From 3dacde91e1537fce54ccce74000fd2a647464668 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 04:13:06 +0000 Subject: [PATCH 876/2232] Merge pull request #299 from dtolnay/output Add -o flag to set output file for cli --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index dce7a5a..caab254 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -2,7 +2,7 @@ #[path = "test.rs"] mod test; -use super::Opt; +use super::{Opt, Output}; use clap::AppSettings; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -39,6 +39,7 @@ fn app() -> App { .arg(arg_cxx_impl_annotations()) .arg(arg_header()) .arg(arg_include()) + .arg(arg_output()) .help_message("Print help information.") .version_message("Print version information."); if let Some(version) = option_env!("CARGO_PKG_VERSION") { @@ -51,6 +52,7 @@ const INPUT: &str = "input"; const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; const HEADER: &str = "header"; const INCLUDE: &str = "include"; +const OUTPUT: &str = "output"; pub(super) fn from_args() -> Opt { let matches = app().get_matches(); @@ -61,6 +63,11 @@ pub(super) fn from_args() -> Opt { include: matches .values_of(INCLUDE) .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()), + output: match matches.value_of_os(OUTPUT) { + None => Output::Stdout, + Some(path) if path == "-" => Output::Stdout, + Some(path) => Output::File(PathBuf::from(path)), + }, } } @@ -114,3 +121,16 @@ into the generated C++ code as #include lines. .validator_os(validate_utf8) .help(HELP) } + +fn arg_output() -> Arg { + const HELP: &str = "\ +Path of file to write as output. Output goes to stdout if -o is +not specified. + "; + Arg::with_name(OUTPUT) + .long(OUTPUT) + .short("o") + .takes_value(true) + .validator_os(validate_utf8) + .help(HELP) +} diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 14b75e6..834b738 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -11,9 +11,11 @@ mod app; mod gen; mod syntax; -use gen::include; +use gen::error::{report, Result}; +use gen::{fs, include}; use std::io::{self, Write}; use std::path::PathBuf; +use std::process; #[derive(Debug)] struct Opt { @@ -21,13 +23,23 @@ struct Opt { header: bool, cxx_impl_annotations: Option, include: Vec, + output: Output, } -fn write(content: impl AsRef<[u8]>) { - let _ = io::stdout().lock().write_all(content.as_ref()); +#[derive(Debug)] +enum Output { + Stdout, + File(PathBuf), } fn main() { + if let Err(err) = try_main() { + let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err)); + process::exit(1); + } +} + +fn try_main() -> Result<()> { let opt = app::from_args(); let gen = gen::Opt { @@ -37,10 +49,24 @@ fn main() { gen_implementation: !opt.header, }; - match (opt.input, opt.header) { - (Some(input), true) => write(gen::generate_from_path(&input, &gen).header), - (Some(input), false) => write(gen::generate_from_path(&input, &gen).implementation), - (None, true) => write(include::HEADER), + let content; + let content = match (opt.input, opt.header) { + (Some(input), true) => { + content = gen::generate_from_path(&input, &gen).header; + content.as_slice() + } + (Some(input), false) => { + content = gen::generate_from_path(&input, &gen).implementation; + content.as_slice() + } + (None, true) => include::HEADER.as_bytes(), (None, false) => unreachable!(), // enforced by required_unless + }; + + match opt.output { + Output::Stdout => drop(io::stdout().write_all(content)), + Output::File(path) => fs::write(path, content)?, } + + Ok(()) } diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 110e319..76101d9 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -31,6 +31,10 @@ OPTIONS: parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. \x20 + -o, --output + Path of file to write as output. Output goes to stdout if -o is + not specified. + \x20 -V, --version Print version information. "; From 3384c14eedb4c21793336ab5c80931ce27abfc43 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 04:26:56 +0000 Subject: [PATCH 877/2232] Import from libcore where possible --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 95d560e..5780e3e 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; -use std::fmt::{self, Debug, Display}; -use std::slice; -use std::str::{self, Utf8Error}; +use alloc::borrow::Cow; +use core::fmt::{self, Debug, Display}; +use core::slice; +use core::str::{self, Utf8Error}; extern "C" { #[link_name = "cxxbridge04$cxx_string$data"] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index a7d3f2f..78b8f20 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,9 +1,9 @@ use crate::cxx_string::CxxString; -use std::ffi::c_void; -use std::fmt::{self, Display}; -use std::marker::PhantomData; -use std::mem; -use std::ptr; +use core::ffi::c_void; +use core::fmt::{self, Display}; +use core::marker::PhantomData; +use core::mem; +use core::ptr; /// Binding to C++ `std::vector>`. /// diff --git a/src/exception.rs b/src/exception.rs index 125e484..e85bf61 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Debug, Display}; +use core::fmt::{self, Debug, Display}; /// Exception thrown from an `extern "C"` function. #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 8c828a3..fd623e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -370,6 +370,8 @@ #[cfg(built_with_cargo)] extern crate link_cplusplus; +extern crate alloc; + #[macro_use] mod macros; diff --git a/src/opaque.rs b/src/opaque.rs index 0ff6bb9..bad57e7 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -1,4 +1,4 @@ -use std::mem; +use core::mem; // . size = 0 // . align = 1 diff --git a/src/result.rs b/src/result.rs index 72c4959..8b4bb75 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,10 +1,10 @@ use crate::exception::Exception; use crate::rust_str::RustStr; -use std::fmt::Display; -use std::ptr; -use std::result::Result as StdResult; -use std::slice; -use std::str; +use core::fmt::Display; +use core::ptr; +use core::result::Result as StdResult; +use core::slice; +use core::str; #[repr(C)] pub union Result { diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index f509c7f..32f8798 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -1,6 +1,6 @@ -use std::mem; -use std::ptr::NonNull; -use std::slice; +use core::mem; +use core::ptr::NonNull; +use core::slice; // Not necessarily ABI compatible with &[u8]. Codegen performs the translation. #[repr(C)] diff --git a/src/rust_str.rs b/src/rust_str.rs index b944ede..38e5cab 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -1,7 +1,7 @@ -use std::mem; -use std::ptr::NonNull; -use std::slice; -use std::str; +use core::mem; +use core::ptr::NonNull; +use core::slice; +use core::str; // Not necessarily ABI compatible with &str. Codegen performs the translation. #[repr(C)] diff --git a/src/rust_string.rs b/src/rust_string.rs index f0d1df1..d10714b 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,4 +1,4 @@ -use std::mem; +use core::mem; #[repr(C)] pub struct RustString { diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 6f1d567..3ae5d5e 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,5 +1,5 @@ use crate::rust_string::RustString; -use std::mem::ManuallyDrop; +use core::mem::ManuallyDrop; #[repr(C)] pub struct RustVec { diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index 7484d1c..7449f14 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -1,4 +1,4 @@ -use std::slice; +use core::slice; #[export_name = "cxxbridge04$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index 5111c6a..823173a 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -1,5 +1,5 @@ -use std::slice; -use std::str; +use core::slice; +use core::str; #[export_name = "cxxbridge04$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 5c2982d..67c53e1 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -1,7 +1,7 @@ -use std::mem::{ManuallyDrop, MaybeUninit}; -use std::ptr; -use std::slice; -use std::str; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::slice; +use core::str; #[export_name = "cxxbridge04$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index fb49b40..667296f 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,7 +1,7 @@ use crate::rust_string::RustString; use crate::rust_vec::RustVec; -use std::mem; -use std::ptr; +use core::mem; +use core::ptr; macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 0428b5f..2a731d7 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,11 +1,11 @@ use crate::cxx_string::CxxString; use crate::cxx_vector::{self, CxxVector, VectorElement}; -use std::ffi::c_void; -use std::fmt::{self, Debug, Display}; -use std::marker::PhantomData; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::ptr; +use core::ffi::c_void; +use core::fmt::{self, Debug, Display}; +use core::marker::PhantomData; +use core::mem; +use core::ops::{Deref, DerefMut}; +use core::ptr; /// Binding to C++ `std::unique_ptr>`. #[repr(C)] From c5a52f97048e93f2d66b5ae4fe0666d27acf5096 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 04:43:29 +0000 Subject: [PATCH 878/2232] Import prelude types explicitly from alloc --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 5780e3e..2c712f1 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -1,4 +1,5 @@ use alloc::borrow::Cow; +use alloc::string::String; use core::fmt::{self, Debug, Display}; use core::slice; use core::str::{self, Utf8Error}; diff --git a/src/exception.rs b/src/exception.rs index e85bf61..0ffca66 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,3 +1,4 @@ +use alloc::boxed::Box; use core::fmt::{self, Debug, Display}; /// Exception thrown from an `extern "C"` function. diff --git a/src/lib.rs b/src/lib.rs index fd623e8..028800b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,6 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx +#![no_std] #![doc(html_root_url = "https://docs.rs/cxx/0.4.4")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] @@ -371,6 +372,7 @@ extern crate link_cplusplus; extern crate alloc; +extern crate std; #[macro_use] mod macros; diff --git a/src/result.rs b/src/result.rs index 8b4bb75..296d4a2 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,5 +1,7 @@ use crate::exception::Exception; use crate::rust_str::RustStr; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; use core::fmt::Display; use core::ptr; use core::result::Result as StdResult; diff --git a/src/rust_string.rs b/src/rust_string.rs index d10714b..a5fa3f4 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use core::mem; #[repr(C)] diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 3ae5d5e..f1a7741 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,4 +1,6 @@ use crate::rust_string::RustString; +use alloc::string::String; +use alloc::vec::Vec; use core::mem::ManuallyDrop; #[repr(C)] diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index 7449f14..da1c3f9 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -1,3 +1,5 @@ +use alloc::boxed::Box; +use alloc::string::String; use core::slice; #[export_name = "cxxbridge04$exception"] diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 67c53e1..774b824 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -1,3 +1,5 @@ +use alloc::borrow::ToOwned; +use alloc::string::String; use core::mem::{ManuallyDrop, MaybeUninit}; use core::ptr; use core::slice; diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 667296f..2304abf 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,5 +1,6 @@ use crate::rust_string::RustString; use crate::rust_vec::RustVec; +use alloc::vec::Vec; use core::mem; use core::ptr; From ca7b8d6bb748b72685a0220fdd483fa52e448184 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 14 2020 04:51:53 +0000 Subject: [PATCH 879/2232] Merge pull request #300 from dtolnay/core Import from libcore/liballoc where possible --- diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 95d560e..2c712f1 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -1,7 +1,8 @@ -use std::borrow::Cow; -use std::fmt::{self, Debug, Display}; -use std::slice; -use std::str::{self, Utf8Error}; +use alloc::borrow::Cow; +use alloc::string::String; +use core::fmt::{self, Debug, Display}; +use core::slice; +use core::str::{self, Utf8Error}; extern "C" { #[link_name = "cxxbridge04$cxx_string$data"] diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index a7d3f2f..78b8f20 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -1,9 +1,9 @@ use crate::cxx_string::CxxString; -use std::ffi::c_void; -use std::fmt::{self, Display}; -use std::marker::PhantomData; -use std::mem; -use std::ptr; +use core::ffi::c_void; +use core::fmt::{self, Display}; +use core::marker::PhantomData; +use core::mem; +use core::ptr; /// Binding to C++ `std::vector>`. /// diff --git a/src/exception.rs b/src/exception.rs index 125e484..0ffca66 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,4 +1,5 @@ -use std::fmt::{self, Debug, Display}; +use alloc::boxed::Box; +use core::fmt::{self, Debug, Display}; /// Exception thrown from an `extern "C"` function. #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 8c828a3..028800b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,6 +348,7 @@ //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx +#![no_std] #![doc(html_root_url = "https://docs.rs/cxx/0.4.4")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] @@ -370,6 +371,9 @@ #[cfg(built_with_cargo)] extern crate link_cplusplus; +extern crate alloc; +extern crate std; + #[macro_use] mod macros; diff --git a/src/opaque.rs b/src/opaque.rs index 0ff6bb9..bad57e7 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -1,4 +1,4 @@ -use std::mem; +use core::mem; // . size = 0 // . align = 1 diff --git a/src/result.rs b/src/result.rs index 72c4959..296d4a2 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,10 +1,12 @@ use crate::exception::Exception; use crate::rust_str::RustStr; -use std::fmt::Display; -use std::ptr; -use std::result::Result as StdResult; -use std::slice; -use std::str; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use core::fmt::Display; +use core::ptr; +use core::result::Result as StdResult; +use core::slice; +use core::str; #[repr(C)] pub union Result { diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs index f509c7f..32f8798 100644 --- a/src/rust_sliceu8.rs +++ b/src/rust_sliceu8.rs @@ -1,6 +1,6 @@ -use std::mem; -use std::ptr::NonNull; -use std::slice; +use core::mem; +use core::ptr::NonNull; +use core::slice; // Not necessarily ABI compatible with &[u8]. Codegen performs the translation. #[repr(C)] diff --git a/src/rust_str.rs b/src/rust_str.rs index b944ede..38e5cab 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -1,7 +1,7 @@ -use std::mem; -use std::ptr::NonNull; -use std::slice; -use std::str; +use core::mem; +use core::ptr::NonNull; +use core::slice; +use core::str; // Not necessarily ABI compatible with &str. Codegen performs the translation. #[repr(C)] diff --git a/src/rust_string.rs b/src/rust_string.rs index f0d1df1..a5fa3f4 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,4 +1,5 @@ -use std::mem; +use alloc::string::String; +use core::mem; #[repr(C)] pub struct RustString { diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 6f1d567..f1a7741 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,5 +1,7 @@ use crate::rust_string::RustString; -use std::mem::ManuallyDrop; +use alloc::string::String; +use alloc::vec::Vec; +use core::mem::ManuallyDrop; #[repr(C)] pub struct RustVec { diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index 7484d1c..da1c3f9 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -1,4 +1,6 @@ -use std::slice; +use alloc::boxed::Box; +use alloc::string::String; +use core::slice; #[export_name = "cxxbridge04$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index 5111c6a..823173a 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -1,5 +1,5 @@ -use std::slice; -use std::str; +use core::slice; +use core::str; #[export_name = "cxxbridge04$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 5c2982d..774b824 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -1,7 +1,9 @@ -use std::mem::{ManuallyDrop, MaybeUninit}; -use std::ptr; -use std::slice; -use std::str; +use alloc::borrow::ToOwned; +use alloc::string::String; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ptr; +use core::slice; +use core::str; #[export_name = "cxxbridge04$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index fb49b40..2304abf 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -1,7 +1,8 @@ use crate::rust_string::RustString; use crate::rust_vec::RustVec; -use std::mem; -use std::ptr; +use alloc::vec::Vec; +use core::mem; +use core::ptr; macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 0428b5f..2a731d7 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,11 +1,11 @@ use crate::cxx_string::CxxString; use crate::cxx_vector::{self, CxxVector, VectorElement}; -use std::ffi::c_void; -use std::fmt::{self, Debug, Display}; -use std::marker::PhantomData; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::ptr; +use core::ffi::c_void; +use core::fmt::{self, Debug, Display}; +use core::marker::PhantomData; +use core::mem; +use core::ops::{Deref, DerefMut}; +use core::ptr; /// Binding to C++ `std::unique_ptr>`. #[repr(C)] From ffef6bc1ef173e64cf69162c780786cdb22b95d1 Mon Sep 17 00:00:00 2001 From: Nehliin Date: Sep 16 2020 11:28:26 +0000 Subject: [PATCH 880/2232] Make sure MaybeUninit is part of generated code for fn returning Result --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8cc1b1b..a6f6c3e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -217,6 +217,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.string = true; needs_rust_str = true; needs_rust_error = true; + needs_maybe_uninit = true; } for arg in &efn.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { From a76da172115b8a9e82e9ea6b011d16ef9bf6e2a4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 16 2020 15:55:14 +0000 Subject: [PATCH 881/2232] Merge pull request #301 from Nehliin/add-maybeuninit Make sure MaybeUninit is part of generated code for Rust functions returning Result --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8cc1b1b..a6f6c3e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -217,6 +217,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.include.string = true; needs_rust_str = true; needs_rust_error = true; + needs_maybe_uninit = true; } for arg in &efn.args { if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { From da38b7c2a5ae54acbd643b1e343d094749b892f0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 16 2020 15:55:24 +0000 Subject: [PATCH 882/2232] Update case of basetsd.h import to support cross compilation --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 54a359f..1688a4e 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -61,7 +61,7 @@ pub struct Includes { pub type_traits: bool, pub utility: bool, pub vector: bool, - pub base_tsd: bool, + pub basetsd: bool, } impl Includes { @@ -122,9 +122,9 @@ impl Display for Includes { if self.vector { writeln!(f, "#include ")?; } - if self.base_tsd { + if self.basetsd { writeln!(f, "#if defined(_WIN32)")?; - writeln!(f, "#include ")?; + writeln!(f, "#include ")?; writeln!(f, "#endif")?; } Ok(()) diff --git a/gen/src/write.rs b/gen/src/write.rs index a6f6c3e..862be81 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -177,7 +177,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_slice = true; } ty if ty == Isize => { - out.include.base_tsd = true; + out.include.basetsd = true; needs_rust_isize = true; } ty if ty == RustString => { diff --git a/include/cxx.h b/include/cxx.h index 3995924..b2e0eee 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -11,7 +11,7 @@ #include #include #if defined(_WIN32) -#include +#include #endif namespace rust { From f3a518e70e49e8759cad39690719c050324c295e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 16 2020 16:07:46 +0000 Subject: [PATCH 883/2232] Merge pull request #303 from dtolnay/basetsd.h Update case of basetsd.h import to support cross compilation --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 54a359f..1688a4e 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -61,7 +61,7 @@ pub struct Includes { pub type_traits: bool, pub utility: bool, pub vector: bool, - pub base_tsd: bool, + pub basetsd: bool, } impl Includes { @@ -122,9 +122,9 @@ impl Display for Includes { if self.vector { writeln!(f, "#include ")?; } - if self.base_tsd { + if self.basetsd { writeln!(f, "#if defined(_WIN32)")?; - writeln!(f, "#include ")?; + writeln!(f, "#include ")?; writeln!(f, "#endif")?; } Ok(()) diff --git a/gen/src/write.rs b/gen/src/write.rs index a6f6c3e..862be81 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -177,7 +177,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_rust_slice = true; } ty if ty == Isize => { - out.include.base_tsd = true; + out.include.basetsd = true; needs_rust_isize = true; } ty if ty == RustString => { diff --git a/include/cxx.h b/include/cxx.h index 3995924..b2e0eee 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -11,7 +11,7 @@ #include #include #if defined(_WIN32) -#include +#include #endif namespace rust { From e1834e6b86ba8ab3b6251d25e5c287f2b7c44b26 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 16 2020 16:10:13 +0000 Subject: [PATCH 884/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 1dd5a24..d4c77b9 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -7,7 +7,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.59/src/**"]), + srcs = glob(["vendor/cc-1.0.60/src/**"]), visibility = ["PUBLIC"], ) @@ -64,7 +64,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.40/src/**"]), + srcs = glob(["vendor/syn-1.0.41/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index a95bbcb..e5cb808 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -12,7 +12,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.59/src/**"]), + srcs = glob(["vendor/cc-1.0.60/src/**"]), visibility = ["//visibility:public"], ) @@ -69,7 +69,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.40/src/**"]), + srcs = glob(["vendor/syn-1.0.41/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1c9da28..f6ae89d 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -28,9 +28,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" +checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" [[package]] name = "clap" @@ -171,9 +171,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "link-cplusplus" @@ -221,18 +221,18 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" dependencies = [ "proc-macro2", "quote", @@ -258,9 +258,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48105a4deaf74163c017939b45ef7322fba46e8b17281528039b0beb04235e92" +checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d" dependencies = [ "dissimilar", "glob", From 7ff80c44e85a827fbeba4f8911000b65f2e3061e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 16 2020 16:12:02 +0000 Subject: [PATCH 885/2232] Release 0.4.5 --- diff --git a/Cargo.toml b/Cargo.toml index 6613014..95fe14a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.4" # remember to update html_root_url +version = "0.4.5" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.4", path = "macro" } +cxxbridge-macro = { version = "=0.4.5", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.4", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.5", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.4", path = "gen/build" } +cxx-build = { version = "=0.4.5", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index fa4b95f..6c58f78 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.4" +version = "0.4.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 0eb9874..05cdc08 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.4" +version = "0.4.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 2c548e6..e19092c 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.4" +version = "0.4.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 301a750..781cb66 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.4" +version = "0.4.5" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 028800b..9efac51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.4.4")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.5")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index f6ae89d..8aa5a1f 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.4" +version = "0.4.5" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.4" +version = "0.4.5" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.4" +version = "0.4.5" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.4" +version = "0.4.5" [[package]] name = "cxxbridge-macro" -version = "0.4.4" +version = "0.4.5" dependencies = [ "cxx", "proc-macro2", From ca2d3e73804b83b459c8a944fb608961b69148c9 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Sep 19 2020 14:54:20 +0000 Subject: [PATCH 886/2232] Bump gen/lib version to match cxx. We need to stay roughly correlated such that cargo users do not get mismatches between `cxxbridge04$` (generated by the main cxx crate) and `cxxbridge03$` (which is what they get when they fetch cxx-gen 0.1 from cargo). In other words, even if the goal is to rev this library on a different cadence, that cadence needs to be at least as fast as breaking changes in `cxx` itself. --- diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index db6de83..6731d12 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.0.1" +version = "0.4.0" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" From 4b845d64885ae8170a96623c357b2fba7e05ac48 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 19 2020 15:33:12 +0000 Subject: [PATCH 887/2232] Merge pull request #306 from adetaylor/bump-gen-lib-version Bump gen/lib version to match cxx. --- diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index db6de83..6731d12 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.0.1" +version = "0.4.0" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" From ae3026245c8cb0255094be082fe12601c6da9ea5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 19 2020 15:34:32 +0000 Subject: [PATCH 888/2232] Raise cxx-build's dev dependency on cxx-gen --- diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 05cdc08..cfc1b5e 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -18,7 +18,7 @@ quote = { version = "1.0", default-features = false } syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [dev-dependencies] -cxx-gen = { version = "=0.0.1", path = "../lib" } +cxx-gen = { version = "0.4", path = "../lib" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] From d22babaeb43d3329c6ad64d85e792792248f82ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 19 2020 15:35:20 +0000 Subject: [PATCH 889/2232] Update third-party lockfile's cxx-gen to the repo version --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 8aa5a1f..199064b 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.0.1" +version = "0.4.0" dependencies = [ "cc", "codespan-reporting", From 22602b43a8156b6838511e116566daf2b32fb3a5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 21 2020 22:22:37 +0000 Subject: [PATCH 890/2232] Fix return Result> from Rust to C++ --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 862be81..2e2b81f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -775,9 +775,11 @@ fn write_rust_function_shim_impl( write!(out, "extern$"); } write!(out, ")"); - if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { - write!(out, ")"); + if !indirect_return { + if let Some(ret) = &sig.ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + write!(out, ")"); + } } } writeln!(out, ";"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 879431e..1c21770 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -580,42 +580,42 @@ fn expand_rust_function_shim_impl( }; call.extend(quote! { (#(#vars),*) }); - let mut expr = sig - .ret - .as_ref() - .and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => { - Some(quote!(::cxx::private::RustString::from(#call))) - } - Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), - Type::RustVec(vec) => { - if vec.inner == RustString { - Some(quote!(::cxx::private::RustVec::from_vec_string(#call))) - } else { - Some(quote!(::cxx::private::RustVec::from(#call))) - } + let conversion = sig.ret.as_ref().and_then(|ret| match ret { + Type::Ident(ident) if ident == RustString => Some(quote!(::cxx::private::RustString::from)), + Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(::cxx::private::RustVec::from_vec_string)) + } else { + Some(quote!(::cxx::private::RustVec::from)) } - Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), - Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { - None => Some(quote!(::cxx::private::RustString::from_ref(#call))), - Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), - }, - Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { - None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string(#call))), - Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string(#call))), - }, - Type::RustVec(_) => match ty.mutability { - None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), - Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), - }, - _ => None, + } + Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustString::from_ref)), + Some(_) => Some(quote!(::cxx::private::RustString::from_mut)), + }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref)), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)), }, - Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), - Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))), _ => None, - }) - .unwrap_or(call); + }, + Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)), + Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)), + _ => None, + }); + + let mut expr = match conversion { + None => call, + Some(conversion) if !sig.throws => quote!(#conversion(#call)), + Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)), + }; let mut outparam = None; let indirect_return = indirect_return(sig, types); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d6724f5..2829fef 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -152,6 +152,7 @@ pub mod ffi { fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; + fn r_try_return_box() -> Result>; fn r_fail_return_primitive() -> Result; fn r_return_r2(n: usize) -> Box; @@ -334,6 +335,10 @@ fn r_try_return_primitive() -> Result { Ok(2020) } +fn r_try_return_box() -> Result, Error> { + Ok(Box::new(2020)) +} + fn r_fail_return_primitive() -> Result { Err(Error) } From 07c4832b431b5a4d04e02d3383f25f320091a55a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 21 2020 22:34:15 +0000 Subject: [PATCH 891/2232] Merge pull request #311 from dtolnay/result-box Fix return Result> from Rust to C++ [v2] --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 862be81..2e2b81f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -775,9 +775,11 @@ fn write_rust_function_shim_impl( write!(out, "extern$"); } write!(out, ")"); - if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { - write!(out, ")"); + if !indirect_return { + if let Some(ret) = &sig.ret { + if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + write!(out, ")"); + } } } writeln!(out, ";"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 879431e..1c21770 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -580,42 +580,42 @@ fn expand_rust_function_shim_impl( }; call.extend(quote! { (#(#vars),*) }); - let mut expr = sig - .ret - .as_ref() - .and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => { - Some(quote!(::cxx::private::RustString::from(#call))) - } - Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))), - Type::RustVec(vec) => { - if vec.inner == RustString { - Some(quote!(::cxx::private::RustVec::from_vec_string(#call))) - } else { - Some(quote!(::cxx::private::RustVec::from(#call))) - } + let conversion = sig.ret.as_ref().and_then(|ret| match ret { + Type::Ident(ident) if ident == RustString => Some(quote!(::cxx::private::RustString::from)), + Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)), + Type::RustVec(vec) => { + if vec.inner == RustString { + Some(quote!(::cxx::private::RustVec::from_vec_string)) + } else { + Some(quote!(::cxx::private::RustVec::from)) } - Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))), - Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { - None => Some(quote!(::cxx::private::RustString::from_ref(#call))), - Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))), - }, - Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { - None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string(#call))), - Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string(#call))), - }, - Type::RustVec(_) => match ty.mutability { - None => Some(quote!(::cxx::private::RustVec::from_ref(#call))), - Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))), - }, - _ => None, + } + Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)), + Type::Ref(ty) => match &ty.inner { + Type::Ident(ident) if ident == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustString::from_ref)), + Some(_) => Some(quote!(::cxx::private::RustString::from_mut)), + }, + Type::RustVec(vec) if vec.inner == RustString => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)), + }, + Type::RustVec(_) => match ty.mutability { + None => Some(quote!(::cxx::private::RustVec::from_ref)), + Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)), }, - Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))), - Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))), _ => None, - }) - .unwrap_or(call); + }, + Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)), + Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)), + _ => None, + }); + + let mut expr = match conversion { + None => call, + Some(conversion) if !sig.throws => quote!(#conversion(#call)), + Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)), + }; let mut outparam = None; let indirect_return = indirect_return(sig, types); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d6724f5..2829fef 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -152,6 +152,7 @@ pub mod ffi { fn r_try_return_void() -> Result<()>; fn r_try_return_primitive() -> Result; + fn r_try_return_box() -> Result>; fn r_fail_return_primitive() -> Result; fn r_return_r2(n: usize) -> Box; @@ -334,6 +335,10 @@ fn r_try_return_primitive() -> Result { Ok(2020) } +fn r_try_return_box() -> Result, Error> { + Ok(Box::new(2020)) +} + fn r_fail_return_primitive() -> Result { Err(Error) } From 01894f0432d0cd943c94ad4570ab0f5c472fb0ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 21 2020 22:35:23 +0000 Subject: [PATCH 892/2232] Update lockfile --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 199064b..50eb94c 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -150,9 +150,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" dependencies = [ "libc", ] From 71912c3f11c9a5928b6bbfd1d26bad82a1bed3f7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 21 2020 22:36:26 +0000 Subject: [PATCH 893/2232] Release 0.4.6 --- diff --git a/Cargo.toml b/Cargo.toml index 95fe14a..cab9814 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.5" # remember to update html_root_url +version = "0.4.6" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.5", path = "macro" } +cxxbridge-macro = { version = "=0.4.6", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.5", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.6", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.5", path = "gen/build" } +cxx-build = { version = "=0.4.6", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 6c58f78..98cf047 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.5" +version = "0.4.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index cfc1b5e..cfe094c 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.5" +version = "0.4.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index e19092c..ba6d28a 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.5" +version = "0.4.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 6731d12..faa82c2 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.4.0" +version = "0.4.1" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 781cb66..4d17d6c 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.5" +version = "0.4.6" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 9efac51..8990f5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.4.5")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.6")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 50eb94c..c90ed81 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.5" +version = "0.4.6" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.5" +version = "0.4.6" dependencies = [ "cc", "codespan-reporting", @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.4.0" +version = "0.4.1" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.5" +version = "0.4.6" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.5" +version = "0.4.6" [[package]] name = "cxxbridge-macro" -version = "0.4.5" +version = "0.4.6" dependencies = [ "cxx", "proc-macro2", From 3421599ff63fb13ecef493b65d606698468172ee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 22 2020 03:46:31 +0000 Subject: [PATCH 894/2232] Update to dev branch of Buck --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0b9d16..420b7af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,8 @@ jobs: - name: Install Buck run: | mkdir bin - wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/v2019.10.17.01/buck-v2019.10.17.01.pex + # TODO: unfork back to facebook/buck after https://github.com/facebook/buck/pull/2545 has landed + wget -q -O bin/buck https://jitpack.io/com/github/dtolnay/buck/3fedc3867c/buck-3fedc3867c.pex chmod +x bin/buck echo ::add-path::bin - name: Install lld From a8e0534d4f96bc7f7d6347ecdfbb30448773926f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 22 2020 03:46:31 +0000 Subject: [PATCH 895/2232] Use java 11 for Buck --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 420b7af..18589f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,13 +53,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 java-package: jre - name: Install Buck run: | mkdir bin # TODO: unfork back to facebook/buck after https://github.com/facebook/buck/pull/2545 has landed - wget -q -O bin/buck https://jitpack.io/com/github/dtolnay/buck/3fedc3867c/buck-3fedc3867c.pex + wget -q -O bin/buck https://jitpack.io/com/github/dtolnay/buck/3fedc3867c/buck-3fedc3867c-java11.pex chmod +x bin/buck echo ::add-path::bin - name: Install lld From 9ec5ca4d68ead93438faf66eefcce75ce1833da3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 22 2020 05:14:31 +0000 Subject: [PATCH 896/2232] Unfork Buck repo --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18589f1..4b42fe3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,8 +58,7 @@ jobs: - name: Install Buck run: | mkdir bin - # TODO: unfork back to facebook/buck after https://github.com/facebook/buck/pull/2545 has landed - wget -q -O bin/buck https://jitpack.io/com/github/dtolnay/buck/3fedc3867c/buck-3fedc3867c-java11.pex + wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex # dev branch from 2020.09.21 chmod +x bin/buck echo ::add-path::bin - name: Install lld From e4d30f2257473f375b48535cce139734502e9f60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 12:57:22 +0000 Subject: [PATCH 897/2232] Set test size of //tests:test Bazel test Without this: INFO: Analyzed 33 targets (1 packages loaded, 12 targets configured). INFO: Found 32 targets and 1 test target... INFO: Elapsed time: 0.905s, Critical Path: 0.66s INFO: 3 processes: 3 linux-sandbox. INFO: Build completed successfully, 3 total actions //tests:test PASSED in 0.1s WARNING: //tests:test: Test execution time (0.1s excluding execution overhead) outside of range for MODERATE tests. Consider setting timeout="short" or size="small". --- diff --git a/tests/BUILD b/tests/BUILD index 29cc79c..d35bbf7 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -2,6 +2,7 @@ load("//tools/bazel:rust.bzl", "rust_library", "rust_test") rust_test( name = "test", + size = "small", srcs = ["test.rs"], deps = [":cxx_test_suite"], ) From e02c8b3b982a63d116533ddbd5fcef5ebe12da7b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 12:58:09 +0000 Subject: [PATCH 898/2232] Load cc_library from @rules_cc Buildifier warns about this now. Function "cc_library" is not global anymore and needs to be loaded from "@rules_cc//cc:defs.bzl".buildifier(native-cc) --- diff --git a/BUILD b/BUILD index 27c6931..4700e06 100644 --- a/BUILD +++ b/BUILD @@ -1,3 +1,4 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_library( diff --git a/demo/BUILD b/demo/BUILD index 1edc65c..03a6535 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -1,3 +1,4 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") rust_binary( diff --git a/tests/BUILD b/tests/BUILD index d35bbf7..ba763e0 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,3 +1,4 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_library", "rust_test") rust_test( From a827ef7d991268630564f65b70fd406febd0910f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:42 +0000 Subject: [PATCH 899/2232] GitHub Codespaces dev container --- diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..e69c7fc --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,12 @@ +FROM mcr.microsoft.com/vscode/devcontainers/rust:1 + +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends openjdk-11-jdk \ + && rustup default nightly 2>&1 \ + && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ + && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ + && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ + && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ + && chmod +x bin/install-bazel bin/buck bin/buildifier \ + && bin/install-bazel diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bc5e979 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,19 @@ +{ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "extensions": [ + "BazelBuild.vscode-bazel", + "matklad.rust-analyzer", + "vadimcn.vscode-lldb" + ] +} From 3627f8c767b2eded3c0eaab6ea54036b9947f8f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:42 +0000 Subject: [PATCH 900/2232] Launch configuration --- diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..244f5c4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run cxx demo", + "type": "lldb", + "request": "launch", + "cargo": { + "args": ["build", "--manifest-path", "demo/Cargo.toml"], + "filter": { + "name": "demo", + "kind": "bin" + } + } + } + ] +} From 6f511055d2c80ba82948480a7236455373fd9341 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:42 +0000 Subject: [PATCH 901/2232] Exclude target dir from search --- diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8a1c2c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "search.exclude": { + "**/target": true + } +} From 1cef990a2d2c95d2af439b67305f4b3dc9f00bce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:42 +0000 Subject: [PATCH 902/2232] Install lld in devcontainer --- diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e69c7fc..b938bda 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,7 +2,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/rust:1 RUN apt-get update \ && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends openjdk-11-jdk \ + && apt-get -y install --no-install-recommends openjdk-11-jdk lld \ && rustup default nightly 2>&1 \ && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ From 0638d09ddb7231bcb3ae59c10579b08d5d883ab5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:42 +0000 Subject: [PATCH 903/2232] Install watchman in devcontainer --- diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b938bda..d4ab609 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,5 +8,11 @@ RUN apt-get update \ && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ + && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \ && chmod +x bin/install-bazel bin/buck bin/buildifier \ - && bin/install-bazel + && bin/install-bazel \ + && unzip tmp/watchman.zip -d tmp \ + && mv tmp/watchman-v2020.09.21.00-linux/bin/watchman bin \ + && mv tmp/watchman-v2020.09.21.00-linux/lib/* /usr/local/lib \ + && mkdir -p /usr/local/var/run/watchman \ + && rm tmp/watchman.zip From da37e50cff7774aa10be715886aac7735aed31cd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:43 +0000 Subject: [PATCH 904/2232] Add test tasks --- diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..44a2ab7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Cargo test", + "type": "shell", + "command": "cargo test", + "group": "test" + }, + { + "label": "Bazel test", + "type": "shell", + "command": "bazel test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Buck test", + "type": "shell", + "command": "buck test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Vendor", + "type": "shell", + "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor" + } + ] +} From a9a83ab96d4e38d8c5222e1366b5f6623530d22c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:04:43 +0000 Subject: [PATCH 905/2232] Push container to Docker Hub --- diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d4ab609..93cfc05 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,18 +1 @@ -FROM mcr.microsoft.com/vscode/devcontainers/rust:1 - -RUN apt-get update \ - && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends openjdk-11-jdk lld \ - && rustup default nightly 2>&1 \ - && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ - && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ - && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ - && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ - && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \ - && chmod +x bin/install-bazel bin/buck bin/buildifier \ - && bin/install-bazel \ - && unzip tmp/watchman.zip -d tmp \ - && mv tmp/watchman-v2020.09.21.00-linux/bin/watchman bin \ - && mv tmp/watchman-v2020.09.21.00-linux/lib/* /usr/local/lib \ - && mkdir -p /usr/local/var/run/watchman \ - && rm tmp/watchman.zip +FROM dtolnay/devcontainer:latest diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile new file mode 100644 index 0000000..d4ab609 --- /dev/null +++ b/.devcontainer/build.Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/vscode/devcontainers/rust:1 + +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends openjdk-11-jdk lld \ + && rustup default nightly 2>&1 \ + && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ + && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ + && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ + && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ + && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \ + && chmod +x bin/install-bazel bin/buck bin/buildifier \ + && bin/install-bazel \ + && unzip tmp/watchman.zip -d tmp \ + && mv tmp/watchman-v2020.09.21.00-linux/bin/watchman bin \ + && mv tmp/watchman-v2020.09.21.00-linux/lib/* /usr/local/lib \ + && mkdir -p /usr/local/var/run/watchman \ + && rm tmp/watchman.zip From 55a7774ffc018c7c5717040600c67fb0f35f596f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:10:52 +0000 Subject: [PATCH 906/2232] Merge pull request #314 from dtolnay/devcontainer Create devcontainer configuration to use with GitHub Codespaces --- diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..93cfc05 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM dtolnay/devcontainer:latest diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile new file mode 100644 index 0000000..d4ab609 --- /dev/null +++ b/.devcontainer/build.Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/vscode/devcontainers/rust:1 + +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends openjdk-11-jdk lld \ + && rustup default nightly 2>&1 \ + && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ + && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ + && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ + && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ + && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \ + && chmod +x bin/install-bazel bin/buck bin/buildifier \ + && bin/install-bazel \ + && unzip tmp/watchman.zip -d tmp \ + && mv tmp/watchman-v2020.09.21.00-linux/bin/watchman bin \ + && mv tmp/watchman-v2020.09.21.00-linux/lib/* /usr/local/lib \ + && mkdir -p /usr/local/var/run/watchman \ + && rm tmp/watchman.zip diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..bc5e979 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,19 @@ +{ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "extensions": [ + "BazelBuild.vscode-bazel", + "matklad.rust-analyzer", + "vadimcn.vscode-lldb" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..244f5c4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run cxx demo", + "type": "lldb", + "request": "launch", + "cargo": { + "args": ["build", "--manifest-path", "demo/Cargo.toml"], + "filter": { + "name": "demo", + "kind": "bin" + } + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8a1c2c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "search.exclude": { + "**/target": true + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..44a2ab7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Cargo test", + "type": "shell", + "command": "cargo test", + "group": "test" + }, + { + "label": "Bazel test", + "type": "shell", + "command": "bazel test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Buck test", + "type": "shell", + "command": "buck test ...", + "group": "test", + "dependsOn": ["Vendor"] + }, + { + "label": "Vendor", + "type": "shell", + "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor" + } + ] +} From 18df6f8589bcdff6045a5479bceb9939b5048bb1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:13:29 +0000 Subject: [PATCH 907/2232] Format devcontainer json --- diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bc5e979..fef25d4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,19 @@ { - "name": "Rust", - "build": { - "dockerfile": "Dockerfile" - }, - "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], - "settings": { - "terminal.integrated.shell.linux": "/bin/bash", - "lldb.executable": "/usr/bin/lldb", - "files.watcherExclude": { - "**/target/**": true - } - }, - "extensions": [ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "extensions": [ "BazelBuild.vscode-bazel", - "matklad.rust-analyzer", + "matklad.rust-analyzer", "vadimcn.vscode-lldb" - ] + ] } From 5f6cffad4a5cf1f17762dd8d552f1c0f9a85883c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:40:08 +0000 Subject: [PATCH 908/2232] Remove unused import from demo/BUILD --- diff --git a/demo/BUILD b/demo/BUILD index 03a6535..9a46fa2 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -1,5 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") +load("//tools/bazel:rust.bzl", "rust_binary") rust_binary( name = "demo", From 0cd342913ac460a36db921590bd9a3c93a5f43d9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:40:27 +0000 Subject: [PATCH 909/2232] Make buck/bazel generated filepaths match source path with extension --- diff --git a/demo/BUCK b/demo/BUCK index eebe5c2..0640a53 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -22,7 +22,7 @@ cxx_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - out = "generated.h", + out = "src/main.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", type = "cxxbridge", ) @@ -30,16 +30,14 @@ genrule( genrule( name = "gen-source", srcs = ["src/main.rs"], - out = "generated.cc", + out = "src/main.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", type = "cxxbridge", ) cxx_library( name = "include", - exported_headers = { - "src/main.rs.h": ":gen-header", - }, + exported_headers = [":gen-header"], ) cxx_library( diff --git a/demo/BUILD b/demo/BUILD index 9a46fa2..c1fb429 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -23,7 +23,7 @@ cc_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - outs = ["main.rs.h"], + outs = ["src/main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) @@ -31,7 +31,7 @@ genrule( genrule( name = "gen-source", srcs = ["src/main.rs"], - outs = ["generated.cc"], + outs = ["src/main.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) @@ -39,7 +39,6 @@ genrule( cc_library( name = "include", hdrs = [":gen-header"], - include_prefix = "demo/src", ) cc_library( diff --git a/tests/BUCK b/tests/BUCK index 79ae790..4a14f2a 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -37,20 +37,20 @@ cxx_library( genrule( name = "gen-lib-header", srcs = ["ffi/lib.rs"], - out = "lib.rs.h", + out = "ffi/lib.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", ) genrule( name = "gen-lib-source", srcs = ["ffi/lib.rs"], - out = "lib.rs.cc", + out = "ffi/lib.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) genrule( name = "gen-module-source", srcs = ["ffi/module.rs"], - out = "module.rs.cc", + out = "ffi/module.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) diff --git a/tests/BUILD b/tests/BUILD index ba763e0..c690994 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -39,7 +39,7 @@ cc_library( genrule( name = "gen-lib-header", srcs = ["ffi/lib.rs"], - outs = ["lib.rs.h"], + outs = ["ffi/lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) @@ -47,7 +47,7 @@ genrule( genrule( name = "gen-lib-source", srcs = ["ffi/lib.rs"], - outs = ["lib.rs.cc"], + outs = ["ffi/lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) @@ -56,12 +56,13 @@ cc_library( name = "lib-include", hdrs = [":gen-lib-header"], include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", ) genrule( name = "gen-module-source", srcs = ["ffi/module.rs"], - outs = ["module.rs.cc"], + outs = ["ffi/module.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) From 55151b4575a39bf4f43457014e2444a24bc7866c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 13:40:27 +0000 Subject: [PATCH 910/2232] Match bazel's semantics for genrule cmd --- diff --git a/demo/BUCK b/demo/BUCK index 78e75e6..eebe5c2 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -1,3 +1,5 @@ +load("//tools/buck:genrule.bzl", "genrule") + rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), diff --git a/tests/BUCK b/tests/BUCK index a96bfb8..79ae790 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -1,3 +1,5 @@ +load("//tools/buck:genrule.bzl", "genrule") + rust_test( name = "test", srcs = ["test.rs"], diff --git a/tools/buck/genrule.bzl b/tools/buck/genrule.bzl new file mode 100644 index 0000000..b5364b7 --- /dev/null +++ b/tools/buck/genrule.bzl @@ -0,0 +1,8 @@ +def genrule(cmd, **kwargs): + # Resolve a distracting inconsistency between Buck and Bazel. + # Bazel creates the directory for your output file, while Buck expects the + # cmd to create it. + # + # TODO: send this as a PR to Buck, because Bazel's behavior here is better. + cmd = "mkdir -p `dirname ${OUT}`; " + cmd + native.genrule(cmd = cmd, **kwargs) From dbfdbd70f2e8fe9124ce3e88d6851a166c7a1bb8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:00:40 +0000 Subject: [PATCH 911/2232] Merge pull request #315 from dtolnay/build Make buck/bazel generated filepaths match source path with extension --- diff --git a/demo/BUCK b/demo/BUCK index eebe5c2..0640a53 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -22,7 +22,7 @@ cxx_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - out = "generated.h", + out = "src/main.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", type = "cxxbridge", ) @@ -30,16 +30,14 @@ genrule( genrule( name = "gen-source", srcs = ["src/main.rs"], - out = "generated.cc", + out = "src/main.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", type = "cxxbridge", ) cxx_library( name = "include", - exported_headers = { - "src/main.rs.h": ":gen-header", - }, + exported_headers = [":gen-header"], ) cxx_library( diff --git a/demo/BUILD b/demo/BUILD index 9a46fa2..c1fb429 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -23,7 +23,7 @@ cc_library( genrule( name = "gen-header", srcs = ["src/main.rs"], - outs = ["main.rs.h"], + outs = ["src/main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) @@ -31,7 +31,7 @@ genrule( genrule( name = "gen-source", srcs = ["src/main.rs"], - outs = ["generated.cc"], + outs = ["src/main.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) @@ -39,7 +39,6 @@ genrule( cc_library( name = "include", hdrs = [":gen-header"], - include_prefix = "demo/src", ) cc_library( diff --git a/tests/BUCK b/tests/BUCK index 79ae790..4a14f2a 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -37,20 +37,20 @@ cxx_library( genrule( name = "gen-lib-header", srcs = ["ffi/lib.rs"], - out = "lib.rs.h", + out = "ffi/lib.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", ) genrule( name = "gen-lib-source", srcs = ["ffi/lib.rs"], - out = "lib.rs.cc", + out = "ffi/lib.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) genrule( name = "gen-module-source", srcs = ["ffi/module.rs"], - out = "module.rs.cc", + out = "ffi/module.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) diff --git a/tests/BUILD b/tests/BUILD index ba763e0..c690994 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -39,7 +39,7 @@ cc_library( genrule( name = "gen-lib-header", srcs = ["ffi/lib.rs"], - outs = ["lib.rs.h"], + outs = ["ffi/lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", tools = ["//:codegen"], ) @@ -47,7 +47,7 @@ genrule( genrule( name = "gen-lib-source", srcs = ["ffi/lib.rs"], - outs = ["lib.rs.cc"], + outs = ["ffi/lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) @@ -56,12 +56,13 @@ cc_library( name = "lib-include", hdrs = [":gen-lib-header"], include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", ) genrule( name = "gen-module-source", srcs = ["ffi/module.rs"], - outs = ["module.rs.cc"], + outs = ["ffi/module.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) From dc57990e22c65744b8587b5eccaac30ba256c0d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:00:50 +0000 Subject: [PATCH 912/2232] Use more systematic naming for generated code targets --- diff --git a/demo/BUCK b/demo/BUCK index 0640a53..7b476a5 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -4,23 +4,23 @@ rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), deps = [ + ":bridge", ":demo-sys", - ":gen", "//:cxx", ], ) cxx_library( - name = "gen", - srcs = [":gen-source"], + name = "bridge", + srcs = [":bridge/source"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) genrule( - name = "gen-header", + name = "bridge/header", srcs = ["src/main.rs"], out = "src/main.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", @@ -28,7 +28,7 @@ genrule( ) genrule( - name = "gen-source", + name = "bridge/source", srcs = ["src/main.rs"], out = "src/main.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", @@ -36,8 +36,8 @@ genrule( ) cxx_library( - name = "include", - exported_headers = [":gen-header"], + name = "bridge/include", + exported_headers = [":bridge/header"], ) cxx_library( @@ -45,8 +45,8 @@ cxx_library( srcs = ["src/demo.cc"], compiler_flags = ["-std=c++14"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) diff --git a/demo/BUILD b/demo/BUILD index c1fb429..6e07e5e 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -5,23 +5,23 @@ rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), deps = [ + ":bridge", ":demo-sys", - ":gen", "//:cxx", ], ) cc_library( - name = "gen", - srcs = [":gen-source"], + name = "bridge", + srcs = [":bridge/source"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) genrule( - name = "gen-header", + name = "bridge/header", srcs = ["src/main.rs"], outs = ["src/main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -29,7 +29,7 @@ genrule( ) genrule( - name = "gen-source", + name = "bridge/source", srcs = ["src/main.rs"], outs = ["src/main.rs.cc"], cmd = "$(location //:codegen) $< > $@", @@ -37,8 +37,8 @@ genrule( ) cc_library( - name = "include", - hdrs = [":gen-header"], + name = "bridge/include", + hdrs = [":bridge/header"], ) cc_library( @@ -46,8 +46,8 @@ cc_library( srcs = ["src/demo.cc"], copts = ["-std=c++14"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) diff --git a/tests/BUCK b/tests/BUCK index 4a14f2a..a5bd044 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -23,33 +23,33 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-lib-source", - ":gen-module-source", + ":bridge/source", + ":module/source", ], header_namespace = "cxx-test-suite", headers = { - "lib.rs.h": ":gen-lib-header", + "lib.rs.h": ":bridge/header", "tests.h": "ffi/tests.h", }, deps = ["//:core"], ) genrule( - name = "gen-lib-header", + name = "bridge/header", srcs = ["ffi/lib.rs"], out = "ffi/lib.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", ) genrule( - name = "gen-lib-source", + name = "bridge/source", srcs = ["ffi/lib.rs"], out = "ffi/lib.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) genrule( - name = "gen-module-source", + name = "module/source", srcs = ["ffi/module.rs"], out = "ffi/module.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", diff --git a/tests/BUILD b/tests/BUILD index c690994..586affc 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -24,20 +24,20 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-lib-source", - ":gen-module-source", + ":bridge/source", + ":module/source", ], hdrs = ["ffi/tests.h"], include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", deps = [ - ":lib-include", + ":bridge/include", "//:core", ], ) genrule( - name = "gen-lib-header", + name = "bridge/header", srcs = ["ffi/lib.rs"], outs = ["ffi/lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -45,7 +45,7 @@ genrule( ) genrule( - name = "gen-lib-source", + name = "bridge/source", srcs = ["ffi/lib.rs"], outs = ["ffi/lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", @@ -53,14 +53,14 @@ genrule( ) cc_library( - name = "lib-include", - hdrs = [":gen-lib-header"], + name = "bridge/include", + hdrs = [":bridge/header"], include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", ) genrule( - name = "gen-module-source", + name = "module/source", srcs = ["ffi/module.rs"], outs = ["ffi/module.rs.cc"], cmd = "$(location //:codegen) $< > $@", From f337a209c35ff64212fbb97e474a2fbfe702e9d0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:08:01 +0000 Subject: [PATCH 913/2232] Merge pull request #316 from dtolnay/build Use more systematic naming for generated code targets --- diff --git a/demo/BUCK b/demo/BUCK index 0640a53..7b476a5 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -4,23 +4,23 @@ rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), deps = [ + ":bridge", ":demo-sys", - ":gen", "//:cxx", ], ) cxx_library( - name = "gen", - srcs = [":gen-source"], + name = "bridge", + srcs = [":bridge/source"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) genrule( - name = "gen-header", + name = "bridge/header", srcs = ["src/main.rs"], out = "src/main.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", @@ -28,7 +28,7 @@ genrule( ) genrule( - name = "gen-source", + name = "bridge/source", srcs = ["src/main.rs"], out = "src/main.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", @@ -36,8 +36,8 @@ genrule( ) cxx_library( - name = "include", - exported_headers = [":gen-header"], + name = "bridge/include", + exported_headers = [":bridge/header"], ) cxx_library( @@ -45,8 +45,8 @@ cxx_library( srcs = ["src/demo.cc"], compiler_flags = ["-std=c++14"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) diff --git a/demo/BUILD b/demo/BUILD index c1fb429..6e07e5e 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -5,23 +5,23 @@ rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), deps = [ + ":bridge", ":demo-sys", - ":gen", "//:cxx", ], ) cc_library( - name = "gen", - srcs = [":gen-source"], + name = "bridge", + srcs = [":bridge/source"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) genrule( - name = "gen-header", + name = "bridge/header", srcs = ["src/main.rs"], outs = ["src/main.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -29,7 +29,7 @@ genrule( ) genrule( - name = "gen-source", + name = "bridge/source", srcs = ["src/main.rs"], outs = ["src/main.rs.cc"], cmd = "$(location //:codegen) $< > $@", @@ -37,8 +37,8 @@ genrule( ) cc_library( - name = "include", - hdrs = [":gen-header"], + name = "bridge/include", + hdrs = [":bridge/header"], ) cc_library( @@ -46,8 +46,8 @@ cc_library( srcs = ["src/demo.cc"], copts = ["-std=c++14"], deps = [ + ":bridge/include", ":demo-include", - ":include", ], ) diff --git a/tests/BUCK b/tests/BUCK index 4a14f2a..a5bd044 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -23,33 +23,33 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-lib-source", - ":gen-module-source", + ":bridge/source", + ":module/source", ], header_namespace = "cxx-test-suite", headers = { - "lib.rs.h": ":gen-lib-header", + "lib.rs.h": ":bridge/header", "tests.h": "ffi/tests.h", }, deps = ["//:core"], ) genrule( - name = "gen-lib-header", + name = "bridge/header", srcs = ["ffi/lib.rs"], out = "ffi/lib.rs.h", cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", ) genrule( - name = "gen-lib-source", + name = "bridge/source", srcs = ["ffi/lib.rs"], out = "ffi/lib.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", ) genrule( - name = "gen-module-source", + name = "module/source", srcs = ["ffi/module.rs"], out = "ffi/module.rs.cc", cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", diff --git a/tests/BUILD b/tests/BUILD index c690994..586affc 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -24,20 +24,20 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-lib-source", - ":gen-module-source", + ":bridge/source", + ":module/source", ], hdrs = ["ffi/tests.h"], include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", deps = [ - ":lib-include", + ":bridge/include", "//:core", ], ) genrule( - name = "gen-lib-header", + name = "bridge/header", srcs = ["ffi/lib.rs"], outs = ["ffi/lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -45,7 +45,7 @@ genrule( ) genrule( - name = "gen-lib-source", + name = "bridge/source", srcs = ["ffi/lib.rs"], outs = ["ffi/lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", @@ -53,14 +53,14 @@ genrule( ) cc_library( - name = "lib-include", - hdrs = [":gen-lib-header"], + name = "bridge/include", + hdrs = [":bridge/header"], include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", ) genrule( - name = "gen-module-source", + name = "module/source", srcs = ["ffi/module.rs"], outs = ["ffi/module.rs.cc"], cmd = "$(location //:codegen) $< > $@", From 717c7e6aa862883cbb5049d77e5099de9261b74b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:08:10 +0000 Subject: [PATCH 914/2232] Factor out genrules to rust_cxx_bridge.bzl --- diff --git a/demo/BUCK b/demo/BUCK index 7b476a5..846c149 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -1,4 +1,4 @@ -load("//tools/buck:genrule.bzl", "genrule") +load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", @@ -10,34 +10,10 @@ rust_binary( ], ) -cxx_library( +rust_cxx_bridge( name = "bridge", - srcs = [":bridge/source"], - deps = [ - ":bridge/include", - ":demo-include", - ], -) - -genrule( - name = "bridge/header", - srcs = ["src/main.rs"], - out = "src/main.rs.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -genrule( - name = "bridge/source", - srcs = ["src/main.rs"], - out = "src/main.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -cxx_library( - name = "bridge/include", - exported_headers = [":bridge/header"], + src = "src/main.rs", + deps = [":demo-include"], ) cxx_library( diff --git a/demo/BUILD b/demo/BUILD index 6e07e5e..5bc8c77 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_binary") +load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", @@ -11,34 +12,10 @@ rust_binary( ], ) -cc_library( +rust_cxx_bridge( name = "bridge", - srcs = [":bridge/source"], - deps = [ - ":bridge/include", - ":demo-include", - ], -) - -genrule( - name = "bridge/header", - srcs = ["src/main.rs"], - outs = ["src/main.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "bridge/source", - srcs = ["src/main.rs"], - outs = ["src/main.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "bridge/include", - hdrs = [":bridge/header"], + src = "src/main.rs", + deps = [":demo-include"], ) cc_library( diff --git a/tests/BUCK b/tests/BUCK index a5bd044..47fc557 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -1,4 +1,4 @@ -load("//tools/buck:genrule.bzl", "genrule") +load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", @@ -34,23 +34,12 @@ cxx_library( deps = ["//:core"], ) -genrule( - name = "bridge/header", - srcs = ["ffi/lib.rs"], - out = "ffi/lib.rs.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", +rust_cxx_bridge( + name = "bridge", + src = "ffi/lib.rs", ) -genrule( - name = "bridge/source", - srcs = ["ffi/lib.rs"], - out = "ffi/lib.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", -) - -genrule( - name = "module/source", - srcs = ["ffi/module.rs"], - out = "ffi/module.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", +rust_cxx_bridge( + name = "module", + src = "ffi/module.rs", ) diff --git a/tests/BUILD b/tests/BUILD index 586affc..68e7be3 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_library", "rust_test") +load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", @@ -36,33 +37,18 @@ cc_library( ], ) -genrule( - name = "bridge/header", - srcs = ["ffi/lib.rs"], - outs = ["ffi/lib.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "bridge/source", - srcs = ["ffi/lib.rs"], - outs = ["ffi/lib.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "bridge/include", - hdrs = [":bridge/header"], +rust_cxx_bridge( + name = "bridge", + src = "ffi/lib.rs", include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", + deps = [":impl"], ) -genrule( - name = "module/source", - srcs = ["ffi/module.rs"], - outs = ["ffi/module.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], +rust_cxx_bridge( + name = "module", + src = "ffi/module.rs", + include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", + deps = [":impl"], ) diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl new file mode 100644 index 0000000..7f3f958 --- /dev/null +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -0,0 +1,36 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +def rust_cxx_bridge( + name, + src, + include_prefix = None, + strip_include_prefix = None, + deps = []): + native.genrule( + name = "%s/header" % name, + srcs = [src], + outs = [src + ".h"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], + ) + + native.genrule( + name = "%s/source" % name, + srcs = [src], + outs = [src + ".cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], + ) + + cc_library( + name = name, + srcs = [":%s/source" % name], + deps = deps + [":%s/include" % name], + ) + + cc_library( + name = "%s/include" % name, + hdrs = [":%s/header" % name], + include_prefix = include_prefix, + strip_include_prefix = strip_include_prefix, + ) diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl new file mode 100644 index 0000000..5fa530b --- /dev/null +++ b/tools/buck/rust_cxx_bridge.bzl @@ -0,0 +1,30 @@ +load("//tools/buck:genrule.bzl", "genrule") + +def rust_cxx_bridge(name, src, deps = []): + genrule( + name = "%s/header" % name, + srcs = [src], + out = src + ".h", + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", + ) + + genrule( + name = "%s/source" % name, + srcs = [src], + out = src + ".cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", + ) + + cxx_library( + name = name, + srcs = [":%s/source" % name], + preferred_linkage = "static", + deps = deps + [":%s/include" % name], + ) + + cxx_library( + name = "%s/include" % name, + exported_headers = [":%s/header" % name], + ) From 6140a3f61db9ca51c25e187773a3a555a7ce53ac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:14:56 +0000 Subject: [PATCH 915/2232] Merge pull request #317 from dtolnay/build Factor out genrules to rust_cxx_bridge.bzl --- diff --git a/demo/BUCK b/demo/BUCK index 7b476a5..846c149 100644 --- a/demo/BUCK +++ b/demo/BUCK @@ -1,4 +1,4 @@ -load("//tools/buck:genrule.bzl", "genrule") +load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", @@ -10,34 +10,10 @@ rust_binary( ], ) -cxx_library( +rust_cxx_bridge( name = "bridge", - srcs = [":bridge/source"], - deps = [ - ":bridge/include", - ":demo-include", - ], -) - -genrule( - name = "bridge/header", - srcs = ["src/main.rs"], - out = "src/main.rs.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -genrule( - name = "bridge/source", - srcs = ["src/main.rs"], - out = "src/main.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - type = "cxxbridge", -) - -cxx_library( - name = "bridge/include", - exported_headers = [":bridge/header"], + src = "src/main.rs", + deps = [":demo-include"], ) cxx_library( diff --git a/demo/BUILD b/demo/BUILD index 6e07e5e..5bc8c77 100644 --- a/demo/BUILD +++ b/demo/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_binary") +load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", @@ -11,34 +12,10 @@ rust_binary( ], ) -cc_library( +rust_cxx_bridge( name = "bridge", - srcs = [":bridge/source"], - deps = [ - ":bridge/include", - ":demo-include", - ], -) - -genrule( - name = "bridge/header", - srcs = ["src/main.rs"], - outs = ["src/main.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "bridge/source", - srcs = ["src/main.rs"], - outs = ["src/main.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "bridge/include", - hdrs = [":bridge/header"], + src = "src/main.rs", + deps = [":demo-include"], ) cc_library( diff --git a/tests/BUCK b/tests/BUCK index a5bd044..47fc557 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -1,4 +1,4 @@ -load("//tools/buck:genrule.bzl", "genrule") +load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", @@ -34,23 +34,12 @@ cxx_library( deps = ["//:core"], ) -genrule( - name = "bridge/header", - srcs = ["ffi/lib.rs"], - out = "ffi/lib.rs.h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", +rust_cxx_bridge( + name = "bridge", + src = "ffi/lib.rs", ) -genrule( - name = "bridge/source", - srcs = ["ffi/lib.rs"], - out = "ffi/lib.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", -) - -genrule( - name = "module/source", - srcs = ["ffi/module.rs"], - out = "ffi/module.rs.cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", +rust_cxx_bridge( + name = "module", + src = "ffi/module.rs", ) diff --git a/tests/BUILD b/tests/BUILD index 586affc..68e7be3 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//tools/bazel:rust.bzl", "rust_library", "rust_test") +load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", @@ -36,33 +37,18 @@ cc_library( ], ) -genrule( - name = "bridge/header", - srcs = ["ffi/lib.rs"], - outs = ["ffi/lib.rs.h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], -) - -genrule( - name = "bridge/source", - srcs = ["ffi/lib.rs"], - outs = ["ffi/lib.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], -) - -cc_library( - name = "bridge/include", - hdrs = [":bridge/header"], +rust_cxx_bridge( + name = "bridge", + src = "ffi/lib.rs", include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", + deps = [":impl"], ) -genrule( - name = "module/source", - srcs = ["ffi/module.rs"], - outs = ["ffi/module.rs.cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], +rust_cxx_bridge( + name = "module", + src = "ffi/module.rs", + include_prefix = "cxx-test-suite", + strip_include_prefix = "ffi", + deps = [":impl"], ) diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl new file mode 100644 index 0000000..7f3f958 --- /dev/null +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -0,0 +1,36 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +def rust_cxx_bridge( + name, + src, + include_prefix = None, + strip_include_prefix = None, + deps = []): + native.genrule( + name = "%s/header" % name, + srcs = [src], + outs = [src + ".h"], + cmd = "$(location //:codegen) --header $< > $@", + tools = ["//:codegen"], + ) + + native.genrule( + name = "%s/source" % name, + srcs = [src], + outs = [src + ".cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], + ) + + cc_library( + name = name, + srcs = [":%s/source" % name], + deps = deps + [":%s/include" % name], + ) + + cc_library( + name = "%s/include" % name, + hdrs = [":%s/header" % name], + include_prefix = include_prefix, + strip_include_prefix = strip_include_prefix, + ) diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl new file mode 100644 index 0000000..5fa530b --- /dev/null +++ b/tools/buck/rust_cxx_bridge.bzl @@ -0,0 +1,30 @@ +load("//tools/buck:genrule.bzl", "genrule") + +def rust_cxx_bridge(name, src, deps = []): + genrule( + name = "%s/header" % name, + srcs = [src], + out = src + ".h", + cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + type = "cxxbridge", + ) + + genrule( + name = "%s/source" % name, + srcs = [src], + out = src + ".cc", + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + type = "cxxbridge", + ) + + cxx_library( + name = name, + srcs = [":%s/source" % name], + preferred_linkage = "static", + deps = deps + [":%s/include" % name], + ) + + cxx_library( + name = "%s/include" % name, + exported_headers = [":%s/header" % name], + ) From e2e47a39a67645e541aada758553a93ef1a76181 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:30:24 +0000 Subject: [PATCH 916/2232] Assume header output for paths ending in .h --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index caab254..44e30bb 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -102,9 +102,11 @@ these C++ functions in another. } fn arg_header() -> Arg { - Arg::with_name(HEADER) - .long(HEADER) - .help("Emit header with declarations only.") + const HELP: &str = "\ +Emit header with declarations only. Optional if using `-o` with +a path ending in `.h`. + "; + Arg::with_name(HEADER).long(HEADER).help(HELP) } fn arg_include() -> Arg { diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 834b738..12e173e 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -9,10 +9,12 @@ mod app; mod gen; +mod output; mod syntax; -use gen::error::{report, Result}; -use gen::{fs, include}; +use crate::gen::error::{report, Result}; +use crate::gen::{fs, include}; +use crate::output::Output; use std::io::{self, Write}; use std::path::PathBuf; use std::process; @@ -26,12 +28,6 @@ struct Opt { output: Output, } -#[derive(Debug)] -enum Output { - Stdout, - File(PathBuf), -} - fn main() { if let Err(err) = try_main() { let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err)); @@ -42,15 +38,17 @@ fn main() { fn try_main() -> Result<()> { let opt = app::from_args(); + let gen_header = opt.header || opt.output.ends_with(".h"); + let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, - gen_header: opt.header, - gen_implementation: !opt.header, + gen_header, + gen_implementation: !gen_header, }; let content; - let content = match (opt.input, opt.header) { + let content = match (opt.input, gen_header) { (Some(input), true) => { content = gen::generate_from_path(&input, &gen).header; content.as_slice() diff --git a/gen/cmd/src/output.rs b/gen/cmd/src/output.rs new file mode 100644 index 0000000..a46581b --- /dev/null +++ b/gen/cmd/src/output.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; + +#[derive(Debug)] +pub(crate) enum Output { + Stdout, + File(PathBuf), +} + +impl Output { + pub(crate) fn ends_with(&self, suffix: &str) -> bool { + match self { + Output::Stdout => false, + Output::File(path) => path.to_string_lossy().ends_with(suffix), + } + } +} From cf641edb27d7cd9d047ab6d43e9474db71aec3c5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:44:21 +0000 Subject: [PATCH 917/2232] Update help text test --- diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 76101d9..2e85fac 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -24,8 +24,9 @@ OPTIONS: Print help information. --header - Emit header with declarations only. - + Emit header with declarations only. Optional if using `-o` with + a path ending in `.h`. + \x20 -i, --include ... Any additional headers to #include. The cxxbridge tool does not parse or even require the given paths to exist; they simply go From a5cd6c8133be4a84583b97f6e72929b0bfbdf3bc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:51:16 +0000 Subject: [PATCH 918/2232] Merge pull request #318 from dtolnay/_h Assume header output for paths ending in .h --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index caab254..44e30bb 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -102,9 +102,11 @@ these C++ functions in another. } fn arg_header() -> Arg { - Arg::with_name(HEADER) - .long(HEADER) - .help("Emit header with declarations only.") + const HELP: &str = "\ +Emit header with declarations only. Optional if using `-o` with +a path ending in `.h`. + "; + Arg::with_name(HEADER).long(HEADER).help(HELP) } fn arg_include() -> Arg { diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 834b738..12e173e 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -9,10 +9,12 @@ mod app; mod gen; +mod output; mod syntax; -use gen::error::{report, Result}; -use gen::{fs, include}; +use crate::gen::error::{report, Result}; +use crate::gen::{fs, include}; +use crate::output::Output; use std::io::{self, Write}; use std::path::PathBuf; use std::process; @@ -26,12 +28,6 @@ struct Opt { output: Output, } -#[derive(Debug)] -enum Output { - Stdout, - File(PathBuf), -} - fn main() { if let Err(err) = try_main() { let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err)); @@ -42,15 +38,17 @@ fn main() { fn try_main() -> Result<()> { let opt = app::from_args(); + let gen_header = opt.header || opt.output.ends_with(".h"); + let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, - gen_header: opt.header, - gen_implementation: !opt.header, + gen_header, + gen_implementation: !gen_header, }; let content; - let content = match (opt.input, opt.header) { + let content = match (opt.input, gen_header) { (Some(input), true) => { content = gen::generate_from_path(&input, &gen).header; content.as_slice() diff --git a/gen/cmd/src/output.rs b/gen/cmd/src/output.rs new file mode 100644 index 0000000..a46581b --- /dev/null +++ b/gen/cmd/src/output.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; + +#[derive(Debug)] +pub(crate) enum Output { + Stdout, + File(PathBuf), +} + +impl Output { + pub(crate) fn ends_with(&self, suffix: &str) -> bool { + match self { + Output::Stdout => false, + Output::File(path) => path.to_string_lossy().ends_with(suffix), + } + } +} diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 76101d9..2e85fac 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -24,8 +24,9 @@ OPTIONS: Print help information. --header - Emit header with declarations only. - + Emit header with declarations only. Optional if using `-o` with + a path ending in `.h`. + \x20 -i, --include ... Any additional headers to #include. The cxxbridge tool does not parse or even require the given paths to exist; they simply go From 6427614d945ffd605d7483ba5cc13e681e6f464f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:51:27 +0000 Subject: [PATCH 919/2232] Move arg parsing out of big struct literal --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 44e30bb..7532668 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -56,18 +56,25 @@ const OUTPUT: &str = "output"; pub(super) fn from_args() -> Opt { let matches = app().get_matches(); + + let input = matches.value_of_os(INPUT).map(PathBuf::from); + let cxx_impl_annotations = matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned); + let header = matches.is_present(HEADER); + let include = matches + .values_of(INCLUDE) + .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()); + let output = match matches.value_of_os(OUTPUT) { + None => Output::Stdout, + Some(path) if path == "-" => Output::Stdout, + Some(path) => Output::File(PathBuf::from(path)), + }; + Opt { - input: matches.value_of_os(INPUT).map(PathBuf::from), - cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned), - header: matches.is_present(HEADER), - include: matches - .values_of(INCLUDE) - .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()), - output: match matches.value_of_os(OUTPUT) { - None => Output::Stdout, - Some(path) if path == "-" => Output::Stdout, - Some(path) => Output::File(PathBuf::from(path)), - }, + input, + cxx_impl_annotations, + header, + include, + output, } } From 13213b7a85cc1b5dcac0848bb664f34d463ac7e7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 14:51:27 +0000 Subject: [PATCH 920/2232] Simplify parsing of include flags --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 7532668..939bb44 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -62,7 +62,9 @@ pub(super) fn from_args() -> Opt { let header = matches.is_present(HEADER); let include = matches .values_of(INCLUDE) - .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()); + .unwrap_or_default() + .map(str::to_owned) + .collect(); let output = match matches.value_of_os(OUTPUT) { None => Output::Stdout, Some(path) if path == "-" => Output::Stdout, From f027756ba7f6494cde572d3573c999c47e0bdc93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 15:38:22 +0000 Subject: [PATCH 921/2232] Handle multiple outputs from the same cxxbridge invocation --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 939bb44..152f11a 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -65,18 +65,25 @@ pub(super) fn from_args() -> Opt { .unwrap_or_default() .map(str::to_owned) .collect(); - let output = match matches.value_of_os(OUTPUT) { - None => Output::Stdout, - Some(path) if path == "-" => Output::Stdout, - Some(path) => Output::File(PathBuf::from(path)), - }; + + let mut outputs = Vec::new(); + for path in matches.values_of_os(OUTPUT).unwrap_or_default() { + outputs.push(if path == "-" { + Output::Stdout + } else { + Output::File(PathBuf::from(path)) + }); + } + if outputs.is_empty() { + outputs.push(Output::Stdout); + } Opt { input, cxx_impl_annotations, header, include, - output, + outputs, } } @@ -142,6 +149,7 @@ not specified. .long(OUTPUT) .short("o") .takes_value(true) + .multiple(true) .validator_os(validate_utf8) .help(HELP) } diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 12e173e..f0fd9b4 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -25,7 +25,7 @@ struct Opt { header: bool, cxx_impl_annotations: Option, include: Vec, - output: Output, + outputs: Vec, } fn main() { @@ -35,35 +35,54 @@ fn main() { } } +enum Kind { + GeneratedHeader, + GeneratedImplementation, + Header, +} + fn try_main() -> Result<()> { let opt = app::from_args(); - let gen_header = opt.header || opt.output.ends_with(".h"); + let mut outputs = Vec::new(); + let mut gen_header = false; + let mut gen_implementation = false; + for output in opt.outputs { + let kind = if opt.input.is_none() { + Kind::Header + } else if opt.header || output.ends_with(".h") { + gen_header = true; + Kind::GeneratedHeader + } else { + gen_implementation = true; + Kind::GeneratedImplementation + }; + outputs.push((output, kind)); + } let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, gen_header, - gen_implementation: !gen_header, + gen_implementation, }; - let content; - let content = match (opt.input, gen_header) { - (Some(input), true) => { - content = gen::generate_from_path(&input, &gen).header; - content.as_slice() - } - (Some(input), false) => { - content = gen::generate_from_path(&input, &gen).implementation; - content.as_slice() - } - (None, true) => include::HEADER.as_bytes(), - (None, false) => unreachable!(), // enforced by required_unless + let generated_code = if let Some(input) = opt.input { + gen::generate_from_path(&input, &gen) + } else { + Default::default() }; - match opt.output { - Output::Stdout => drop(io::stdout().write_all(content)), - Output::File(path) => fs::write(path, content)?, + for (output, kind) in outputs { + let content = match kind { + Kind::GeneratedHeader => &generated_code.header, + Kind::GeneratedImplementation => &generated_code.implementation, + Kind::Header => include::HEADER.as_bytes(), + }; + match output { + Output::Stdout => drop(io::stdout().write_all(content)), + Output::File(path) => fs::write(path, content)?, + } } Ok(()) diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 2e85fac..17023e4 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -32,7 +32,7 @@ OPTIONS: parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. \x20 - -o, --output + -o, --output ... Path of file to write as output. Output goes to stdout if -o is not specified. \x20 diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 9578fe8..8522715 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -50,6 +50,7 @@ pub struct Opt { } /// Results of code generation. +#[derive(Default)] pub struct GeneratedCode { /// The bytes of a C++ header file. pub header: Vec, From 950fb211a80de163a8b19990a9d3f3ba221455a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 15:46:40 +0000 Subject: [PATCH 922/2232] Merge pull request #319 from dtolnay/multiple Handle multiple outputs from the same cxxbridge invocation --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 939bb44..152f11a 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -65,18 +65,25 @@ pub(super) fn from_args() -> Opt { .unwrap_or_default() .map(str::to_owned) .collect(); - let output = match matches.value_of_os(OUTPUT) { - None => Output::Stdout, - Some(path) if path == "-" => Output::Stdout, - Some(path) => Output::File(PathBuf::from(path)), - }; + + let mut outputs = Vec::new(); + for path in matches.values_of_os(OUTPUT).unwrap_or_default() { + outputs.push(if path == "-" { + Output::Stdout + } else { + Output::File(PathBuf::from(path)) + }); + } + if outputs.is_empty() { + outputs.push(Output::Stdout); + } Opt { input, cxx_impl_annotations, header, include, - output, + outputs, } } @@ -142,6 +149,7 @@ not specified. .long(OUTPUT) .short("o") .takes_value(true) + .multiple(true) .validator_os(validate_utf8) .help(HELP) } diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index 12e173e..f0fd9b4 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -25,7 +25,7 @@ struct Opt { header: bool, cxx_impl_annotations: Option, include: Vec, - output: Output, + outputs: Vec, } fn main() { @@ -35,35 +35,54 @@ fn main() { } } +enum Kind { + GeneratedHeader, + GeneratedImplementation, + Header, +} + fn try_main() -> Result<()> { let opt = app::from_args(); - let gen_header = opt.header || opt.output.ends_with(".h"); + let mut outputs = Vec::new(); + let mut gen_header = false; + let mut gen_implementation = false; + for output in opt.outputs { + let kind = if opt.input.is_none() { + Kind::Header + } else if opt.header || output.ends_with(".h") { + gen_header = true; + Kind::GeneratedHeader + } else { + gen_implementation = true; + Kind::GeneratedImplementation + }; + outputs.push((output, kind)); + } let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, gen_header, - gen_implementation: !gen_header, + gen_implementation, }; - let content; - let content = match (opt.input, gen_header) { - (Some(input), true) => { - content = gen::generate_from_path(&input, &gen).header; - content.as_slice() - } - (Some(input), false) => { - content = gen::generate_from_path(&input, &gen).implementation; - content.as_slice() - } - (None, true) => include::HEADER.as_bytes(), - (None, false) => unreachable!(), // enforced by required_unless + let generated_code = if let Some(input) = opt.input { + gen::generate_from_path(&input, &gen) + } else { + Default::default() }; - match opt.output { - Output::Stdout => drop(io::stdout().write_all(content)), - Output::File(path) => fs::write(path, content)?, + for (output, kind) in outputs { + let content = match kind { + Kind::GeneratedHeader => &generated_code.header, + Kind::GeneratedImplementation => &generated_code.implementation, + Kind::Header => include::HEADER.as_bytes(), + }; + match output { + Output::Stdout => drop(io::stdout().write_all(content)), + Output::File(path) => fs::write(path, content)?, + } } Ok(()) diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs index 2e85fac..17023e4 100644 --- a/gen/cmd/src/test.rs +++ b/gen/cmd/src/test.rs @@ -32,7 +32,7 @@ OPTIONS: parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. \x20 - -o, --output + -o, --output ... Path of file to write as output. Output goes to stdout if -o is not specified. \x20 diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 9578fe8..8522715 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -50,6 +50,7 @@ pub struct Opt { } /// Results of code generation. +#[derive(Default)] pub struct GeneratedCode { /// The bytes of a C++ header file. pub header: Vec, From 93637cac19ea63a4594b2ffeadf9c0f341f66523 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 19:58:58 +0000 Subject: [PATCH 923/2232] Add CxxVector::as_slice -> &[T] --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1c21770..955ad5f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -859,12 +859,12 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { } unsafe { __vector_size(v) } } - unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> &Self { + unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> *const Self { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem; } - &*__get_unchecked(v, pos) + __get_unchecked(v, pos) } fn __unique_ptr_null() -> *mut ::std::ffi::c_void { extern "C" { diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 78b8f20..3ab3193 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -4,6 +4,7 @@ use core::fmt::{self, Display}; use core::marker::PhantomData; use core::mem; use core::ptr; +use core::slice; /// Binding to C++ `std::vector>`. /// @@ -44,7 +45,7 @@ where /// out of bounds. pub fn get(&self, pos: usize) -> Option<&T> { if pos < self.len() { - Some(unsafe { T::__get_unchecked(self, pos) }) + Some(unsafe { self.get_unchecked(pos) }) } else { None } @@ -61,7 +62,18 @@ where /// /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at pub unsafe fn get_unchecked(&self, pos: usize) -> &T { - T::__get_unchecked(self, pos) + &*T::__get_unchecked(self, pos) + } + + /// Returns a slice to the underlying contiguous array of elements. + pub fn as_slice(&self) -> &[T] { + let len = self.len(); + if len == 0 { + <&[T]>::default() + } else { + let ptr = unsafe { T::__get_unchecked(self, 0) }; + unsafe { slice::from_raw_parts(ptr, len) } + } } } @@ -122,7 +134,7 @@ where pub unsafe trait VectorElement: Sized { const __NAME: &'static dyn Display; fn __vector_size(v: &CxxVector) -> usize; - unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> *const Self; fn __unique_ptr_null() -> *mut c_void; unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; @@ -145,14 +157,14 @@ macro_rules! impl_vector_element { } unsafe { __vector_size(v) } } - unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { + unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty { extern "C" { attr! { #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } - &*__get_unchecked(v, pos) + __get_unchecked(v, pos) } fn __unique_ptr_null() -> *mut c_void { extern "C" { From 5df0a71dcf5a989bfc578bc9a6718b609b6062b0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 19:58:58 +0000 Subject: [PATCH 924/2232] Add test for CxxVector::as_slice --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 2829fef..73d6376 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -6,7 +6,7 @@ pub mod module; -use cxx::{CxxString, UniquePtr}; +use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] @@ -144,6 +144,7 @@ pub mod ffi { fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + fn r_take_ref_vector(v: &CxxVector); fn r_take_rust_vec(v: Vec); fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); @@ -307,6 +308,11 @@ fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } +fn r_take_ref_vector(v: &CxxVector) { + let slice = v.as_slice(); + assert_eq!(slice, [20, 2, 0]); +} + fn r_take_rust_vec(v: Vec) { let _ = v; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8dd16e8..a107c48 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -399,6 +399,7 @@ extern "C" const char *cxx_run_test() noexcept { r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + r_take_ref_vector(std::vector{20, 2, 0}); r_take_enum(Enum::AVal); ASSERT(r_try_return_primitive() == 2020); From a5a14ce1583d0c31019d16511cfdf485666a1a95 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 20:02:40 +0000 Subject: [PATCH 925/2232] Add explanation of as_slice implementation --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 3ab3193..9d04cb5 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -69,6 +69,12 @@ where pub fn as_slice(&self) -> &[T] { let len = self.len(); if len == 0 { + // The slice::from_raw_parts in the other branch requires a nonnull + // and properly aligned data ptr. C++ standard does not guarantee + // that data() on a vector with size 0 would return a nonnull + // pointer or sufficiently aligned pointer, so using it would be + // undefined behavior. Create our own empty slice in Rust instead + // which upholds the invariants. <&[T]>::default() } else { let ptr = unsafe { T::__get_unchecked(self, 0) }; From 80631e93ef59f4db2394e0478ecdb0154fd75f0b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 20:07:30 +0000 Subject: [PATCH 926/2232] Add test involving empty vector --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 73d6376..9ace1f2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -145,6 +145,7 @@ pub mod ffi { fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); fn r_take_ref_vector(v: &CxxVector); + fn r_take_ref_empty_vector(v: &CxxVector); fn r_take_rust_vec(v: Vec); fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); @@ -313,6 +314,11 @@ fn r_take_ref_vector(v: &CxxVector) { assert_eq!(slice, [20, 2, 0]); } +fn r_take_ref_empty_vector(v: &CxxVector) { + assert!(v.as_slice().is_empty()); + assert!(v.is_empty()); +} + fn r_take_rust_vec(v: Vec) { let _ = v; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index a107c48..1f88d8b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -400,6 +400,10 @@ extern "C" const char *cxx_run_test() noexcept { r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); r_take_ref_vector(std::vector{20, 2, 0}); + std::vector empty_vector; + r_take_ref_empty_vector(empty_vector); + empty_vector.reserve(10); + r_take_ref_empty_vector(empty_vector); r_take_enum(Enum::AVal); ASSERT(r_try_return_primitive() == 2020); From 4cbd8f4e13767d66244630ac6b78e3d067c59280 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 20:13:27 +0000 Subject: [PATCH 927/2232] Merge pull request #322 from dtolnay/as_slice Add API to get slice from CxxVector --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1c21770..955ad5f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -859,12 +859,12 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { } unsafe { __vector_size(v) } } - unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> &Self { + unsafe fn __get_unchecked(v: &::cxx::CxxVector, pos: usize) -> *const Self { extern "C" { #[link_name = #link_get_unchecked] fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem; } - &*__get_unchecked(v, pos) + __get_unchecked(v, pos) } fn __unique_ptr_null() -> *mut ::std::ffi::c_void { extern "C" { diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 78b8f20..9d04cb5 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -4,6 +4,7 @@ use core::fmt::{self, Display}; use core::marker::PhantomData; use core::mem; use core::ptr; +use core::slice; /// Binding to C++ `std::vector>`. /// @@ -44,7 +45,7 @@ where /// out of bounds. pub fn get(&self, pos: usize) -> Option<&T> { if pos < self.len() { - Some(unsafe { T::__get_unchecked(self, pos) }) + Some(unsafe { self.get_unchecked(pos) }) } else { None } @@ -61,7 +62,24 @@ where /// /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at pub unsafe fn get_unchecked(&self, pos: usize) -> &T { - T::__get_unchecked(self, pos) + &*T::__get_unchecked(self, pos) + } + + /// Returns a slice to the underlying contiguous array of elements. + pub fn as_slice(&self) -> &[T] { + let len = self.len(); + if len == 0 { + // The slice::from_raw_parts in the other branch requires a nonnull + // and properly aligned data ptr. C++ standard does not guarantee + // that data() on a vector with size 0 would return a nonnull + // pointer or sufficiently aligned pointer, so using it would be + // undefined behavior. Create our own empty slice in Rust instead + // which upholds the invariants. + <&[T]>::default() + } else { + let ptr = unsafe { T::__get_unchecked(self, 0) }; + unsafe { slice::from_raw_parts(ptr, len) } + } } } @@ -122,7 +140,7 @@ where pub unsafe trait VectorElement: Sized { const __NAME: &'static dyn Display; fn __vector_size(v: &CxxVector) -> usize; - unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> &Self; + unsafe fn __get_unchecked(v: &CxxVector, pos: usize) -> *const Self; fn __unique_ptr_null() -> *mut c_void; unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void; unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector; @@ -145,14 +163,14 @@ macro_rules! impl_vector_element { } unsafe { __vector_size(v) } } - unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty { + unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty { extern "C" { attr! { #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } - &*__get_unchecked(v, pos) + __get_unchecked(v, pos) } fn __unique_ptr_null() -> *mut c_void { extern "C" { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 2829fef..9ace1f2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -6,7 +6,7 @@ pub mod module; -use cxx::{CxxString, UniquePtr}; +use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; #[cxx::bridge(namespace = tests)] @@ -144,6 +144,8 @@ pub mod ffi { fn r_take_sliceu8(s: &[u8]); fn r_take_rust_string(s: String); fn r_take_unique_ptr_string(s: UniquePtr); + fn r_take_ref_vector(v: &CxxVector); + fn r_take_ref_empty_vector(v: &CxxVector); fn r_take_rust_vec(v: Vec); fn r_take_rust_vec_string(v: Vec); fn r_take_ref_rust_vec(v: &Vec); @@ -307,6 +309,16 @@ fn r_take_unique_ptr_string(s: UniquePtr) { assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020"); } +fn r_take_ref_vector(v: &CxxVector) { + let slice = v.as_slice(); + assert_eq!(slice, [20, 2, 0]); +} + +fn r_take_ref_empty_vector(v: &CxxVector) { + assert!(v.as_slice().is_empty()); + assert!(v.is_empty()); +} + fn r_take_rust_vec(v: Vec) { let _ = v; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8dd16e8..1f88d8b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -399,6 +399,11 @@ extern "C" const char *cxx_run_test() noexcept { r_take_rust_string(rust::String("2020")); r_take_unique_ptr_string( std::unique_ptr(new std::string("2020"))); + r_take_ref_vector(std::vector{20, 2, 0}); + std::vector empty_vector; + r_take_ref_empty_vector(empty_vector); + empty_vector.reserve(10); + r_take_ref_empty_vector(empty_vector); r_take_enum(Enum::AVal); ASSERT(r_try_return_primitive() == 2020); From b312bb15e3d5bdd094c193591324b84f447f5888 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 20:17:02 +0000 Subject: [PATCH 928/2232] Release 0.4.7 --- diff --git a/Cargo.toml b/Cargo.toml index cab9814..57b4320 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.6" # remember to update html_root_url +version = "0.4.7" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge04" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.6", path = "macro" } +cxxbridge-macro = { version = "=0.4.7", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.6", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.4.7", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.6", path = "gen/build" } +cxx-build = { version = "=0.4.7", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 98cf047..5b4f666 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.6" +version = "0.4.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index cfe094c..675261e 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.6" +version = "0.4.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index ba6d28a..684642c 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.6" +version = "0.4.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4d17d6c..9c151b8 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.6" +version = "0.4.7" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 8990f5b..5f52f49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.4.6")] +#![doc(html_root_url = "https://docs.rs/cxx/0.4.7")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index c90ed81..1c43f70 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.6" +version = "0.4.7" dependencies = [ "cc", "cxx-build", @@ -73,7 +73,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.6" +version = "0.4.7" dependencies = [ "cc", "codespan-reporting", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.6" +version = "0.4.7" dependencies = [ "clap", "codespan-reporting", @@ -116,11 +116,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.6" +version = "0.4.7" [[package]] name = "cxxbridge-macro" -version = "0.4.6" +version = "0.4.7" dependencies = [ "cxx", "proc-macro2", From acc7fb05f9bbf07e7d6e1accce9cdbdf26b91411 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 24 2020 22:10:09 +0000 Subject: [PATCH 929/2232] Simpler expression for making an empty slice --- diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 9d04cb5..a5fe536 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -75,7 +75,7 @@ where // pointer or sufficiently aligned pointer, so using it would be // undefined behavior. Create our own empty slice in Rust instead // which upholds the invariants. - <&[T]>::default() + &[] } else { let ptr = unsafe { T::__get_unchecked(self, 0) }; unsafe { slice::from_raw_parts(ptr, len) } From e53ec043970b5f2a3607ed7f916e061fd7a28333 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 25 2020 03:36:00 +0000 Subject: [PATCH 930/2232] Bypass compiletest on Windows push and pull_request builds --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b42fe3..8ec4224 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,14 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + - name: Determine test suite subset + # Our Windows jobs are the longest running, so exclude the relatively + # slow compiletest from them to speed up end-to-end CI time, except + # during cron builds when no human is presumably waiting on the build. + # The extra coverage is not particularly valuable and we can still + # ensure the test is kept passing on the basis of the scheduled builds. + if: matrix.os == 'windows' && github.event_name != 'schedule' + run: echo '::set-env name=RUSTFLAGS::--cfg skip_ui_tests' - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace --exclude cxx-test-suite diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f9aea23..d2b516f 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ #[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(skip_ui_tests, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new(); From 5a3ddf1f0e82bab3a3013b7f0a574e3598c0014c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 26 2020 00:26:14 +0000 Subject: [PATCH 931/2232] Switch from genrule to run_binary for Bazel --- diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index 7f3f958..6b31b15 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -1,3 +1,4 @@ +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@rules_cc//cc:defs.bzl", "cc_library") def rust_cxx_bridge( @@ -6,20 +7,20 @@ def rust_cxx_bridge( include_prefix = None, strip_include_prefix = None, deps = []): - native.genrule( + run_binary( name = "%s/header" % name, srcs = [src], outs = [src + ".h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], + args = ["$(location %s)" % src, "-o", "$(location %s.h)" % src, "--header"], + tool = "//:codegen", ) - native.genrule( + run_binary( name = "%s/source" % name, srcs = [src], outs = [src + ".cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], + args = ["$(location %s)" % src, "-o", "$(location %s.cc)" % src], + tool = "//:codegen", ) cc_library( From 73d129dfd983c31e86dde30282bdc76e15b06b9c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 26 2020 00:26:25 +0000 Subject: [PATCH 932/2232] Wrap run_binary args --- diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index 6b31b15..497061d 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -11,7 +11,12 @@ def rust_cxx_bridge( name = "%s/header" % name, srcs = [src], outs = [src + ".h"], - args = ["$(location %s)" % src, "-o", "$(location %s.h)" % src, "--header"], + args = [ + "$(location %s)" % src, + "-o", + "$(location %s.h)" % src, + "--header", + ], tool = "//:codegen", ) @@ -19,7 +24,11 @@ def rust_cxx_bridge( name = "%s/source" % name, srcs = [src], outs = [src + ".cc"], - args = ["$(location %s)" % src, "-o", "$(location %s.cc)" % src], + args = [ + "$(location %s)" % src, + "-o", + "$(location %s.cc)" % src, + ], tool = "//:codegen", ) From f0bd14d97f905c4ac666218362aa22addefac5a1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 26 2020 00:39:42 +0000 Subject: [PATCH 933/2232] Merge pull request #326 from dtolnay/run_binary Switch Bazel rules from genrule to run_binary --- diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index 7f3f958..497061d 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -1,3 +1,4 @@ +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@rules_cc//cc:defs.bzl", "cc_library") def rust_cxx_bridge( @@ -6,20 +7,29 @@ def rust_cxx_bridge( include_prefix = None, strip_include_prefix = None, deps = []): - native.genrule( + run_binary( name = "%s/header" % name, srcs = [src], outs = [src + ".h"], - cmd = "$(location //:codegen) --header $< > $@", - tools = ["//:codegen"], + args = [ + "$(location %s)" % src, + "-o", + "$(location %s.h)" % src, + "--header", + ], + tool = "//:codegen", ) - native.genrule( + run_binary( name = "%s/source" % name, srcs = [src], outs = [src + ".cc"], - cmd = "$(location //:codegen) $< > $@", - tools = ["//:codegen"], + args = [ + "$(location %s)" % src, + "-o", + "$(location %s.cc)" % src, + ], + tool = "//:codegen", ) cc_library( From 8b7878ccff292116749be38b8fe0b4f9d8106d20 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 27 2020 21:16:36 +0000 Subject: [PATCH 934/2232] Combine bazel codegen steps into one run of cxxbridge --- diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index 497061d..d4111c6 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -7,26 +7,28 @@ def rust_cxx_bridge( include_prefix = None, strip_include_prefix = None, deps = []): - run_binary( + native.alias( name = "%s/header" % name, - srcs = [src], - outs = [src + ".h"], - args = [ - "$(location %s)" % src, - "-o", - "$(location %s.h)" % src, - "--header", - ], - tool = "//:codegen", + actual = src + ".h", ) - run_binary( + native.alias( name = "%s/source" % name, + actual = src + ".cc", + ) + + run_binary( + name = "%s/generated" % name, srcs = [src], - outs = [src + ".cc"], + outs = [ + src + ".h", + src + ".cc", + ], args = [ "$(location %s)" % src, "-o", + "$(location %s.h)" % src, + "-o", "$(location %s.cc)" % src, ], tool = "//:codegen", @@ -34,13 +36,13 @@ def rust_cxx_bridge( cc_library( name = name, - srcs = [":%s/source" % name], + srcs = [src + ".cc"], deps = deps + [":%s/include" % name], ) cc_library( name = "%s/include" % name, - hdrs = [":%s/header" % name], + hdrs = [src + ".h"], include_prefix = include_prefix, strip_include_prefix = strip_include_prefix, ) From 61dbb45c48fdb03dfca3bb6952b405e67a502813 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 27 2020 21:39:14 +0000 Subject: [PATCH 935/2232] Merge pull request #327 from dtolnay/bazel Combine bazel codegen steps into one run of cxxbridge --- diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index 497061d..d4111c6 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -7,26 +7,28 @@ def rust_cxx_bridge( include_prefix = None, strip_include_prefix = None, deps = []): - run_binary( + native.alias( name = "%s/header" % name, - srcs = [src], - outs = [src + ".h"], - args = [ - "$(location %s)" % src, - "-o", - "$(location %s.h)" % src, - "--header", - ], - tool = "//:codegen", + actual = src + ".h", ) - run_binary( + native.alias( name = "%s/source" % name, + actual = src + ".cc", + ) + + run_binary( + name = "%s/generated" % name, srcs = [src], - outs = [src + ".cc"], + outs = [ + src + ".h", + src + ".cc", + ], args = [ "$(location %s)" % src, "-o", + "$(location %s.h)" % src, + "-o", "$(location %s.cc)" % src, ], tool = "//:codegen", @@ -34,13 +36,13 @@ def rust_cxx_bridge( cc_library( name = name, - srcs = [":%s/source" % name], + srcs = [src + ".cc"], deps = deps + [":%s/include" % name], ) cc_library( name = "%s/include" % name, - hdrs = [":%s/header" % name], + hdrs = [src + ".h"], include_prefix = include_prefix, strip_include_prefix = strip_include_prefix, ) From d1ca05aa1cf403c1cb33a327ca6a2af075ad188e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 27 2020 21:41:08 +0000 Subject: [PATCH 936/2232] Implement buck genrule using -o instead of redirection --- diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl index 5fa530b..ba9d8f1 100644 --- a/tools/buck/rust_cxx_bridge.bzl +++ b/tools/buck/rust_cxx_bridge.bzl @@ -5,7 +5,7 @@ def rust_cxx_bridge(name, src, deps = []): name = "%s/header" % name, srcs = [src], out = src + ".h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", + cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}", type = "cxxbridge", ) @@ -13,7 +13,7 @@ def rust_cxx_bridge(name, src, deps = []): name = "%s/source" % name, srcs = [src], out = src + ".cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}", type = "cxxbridge", ) From 0489527aece22c4819072ce2cecdcbfbdf587839 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 27 2020 21:41:42 +0000 Subject: [PATCH 937/2232] Combine buck codegen steps into one run of cxxbridge --- diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl index ba9d8f1..4acc7c6 100644 --- a/tools/buck/rust_cxx_bridge.bzl +++ b/tools/buck/rust_cxx_bridge.bzl @@ -3,17 +3,21 @@ load("//tools/buck:genrule.bzl", "genrule") def rust_cxx_bridge(name, src, deps = []): genrule( name = "%s/header" % name, - srcs = [src], out = src + ".h", - cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}", - type = "cxxbridge", + cmd = "cp $(location :%s/generated)/generated.h ${OUT}" % name, ) genrule( name = "%s/source" % name, - srcs = [src], out = src + ".cc", - cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}", + cmd = "cp $(location :%s/generated)/generated.cc ${OUT}" % name, + ) + + genrule( + name = "%s/generated" % name, + srcs = [src], + out = ".", + cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}/generated.h -o ${OUT}/generated.cc", type = "cxxbridge", ) From f2fbac79ab6d2880929a3cdc8e021e53b01ee7e1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sep 27 2020 21:47:55 +0000 Subject: [PATCH 938/2232] Merge pull request #328 from dtolnay/buck Combine buck codegen steps into one run of cxxbridge --- diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl index 5fa530b..4acc7c6 100644 --- a/tools/buck/rust_cxx_bridge.bzl +++ b/tools/buck/rust_cxx_bridge.bzl @@ -3,17 +3,21 @@ load("//tools/buck:genrule.bzl", "genrule") def rust_cxx_bridge(name, src, deps = []): genrule( name = "%s/header" % name, - srcs = [src], out = src + ".h", - cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - type = "cxxbridge", + cmd = "cp $(location :%s/generated)/generated.h ${OUT}" % name, ) genrule( name = "%s/source" % name, - srcs = [src], out = src + ".cc", - cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + cmd = "cp $(location :%s/generated)/generated.cc ${OUT}" % name, + ) + + genrule( + name = "%s/generated" % name, + srcs = [src], + out = ".", + cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}/generated.h -o ${OUT}/generated.cc", type = "cxxbridge", ) From c704329d3b9a222b4503218bfa237b36091dfbca Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Sep 30 2020 23:38:06 +0000 Subject: [PATCH 939/2232] Allow aliases and C++ opaque types to be trivial. This change allows aliases: type Foo = bindgen::Bar; and C++ opaque types: type Foo; to declare that they're 'trivial' in the sense that: * They have trivial move constructors * They have no destructors and therefore may be passed and owned by value in Rust. A subsequent commit will add C++ static assertions. This commit is a BREAKING CHANGE as it requires existing ExternTypes to gain a new associated type --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 879431e..fc7c692 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -57,6 +57,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); hidden.extend(expand_type_alias_verify(namespace, alias)); + let ident = &alias.ident; + if types.required_trivial_aliases.contains(ident) { + hidden.extend(expand_type_alias_kind_trivial_verify(alias)); + } } } } @@ -179,6 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { unsafe impl ::cxx::ExternType for #ident { type Id = #type_id; + type Kind = ::cxx::Opaque; } } } @@ -677,6 +682,18 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt } } +fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream { + let ident = &type_alias.ident; + let begin_span = type_alias.type_token.span; + let end_span = type_alias.semi_token.span; + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); + let end = quote_spanned!(end_span=> >); + + quote! { + const _: fn() = #begin #ident, ::cxx::Trivial #end; + } +} + fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { let mut path = String::new(); for name in namespace { diff --git a/src/extern_type.rs b/src/extern_type.rs index 6701ef5..951bc6c 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -104,7 +104,28 @@ pub unsafe trait ExternType { /// # } /// ``` type Id; + + /// Either `kind::Opaque` or `kind::Trivial`. If in doubt, use + /// `kind::Opaque`. + type Kind; +} + +pub(crate) mod kind { + + /// An opaque type which can't be passed or held by value within Rust. + /// For example, a C++ type with a destructor, or a non-trivial move + /// constructor. Rust's strict move semantics mean that we can't own + /// these by value in Rust, but they can still be owned by a + /// `UniquePtr`... + pub struct Opaque; + + /// A type with trivial move constructors and no destructor, which + /// can therefore be owned and moved around in Rust code directly. + pub struct Trivial; } #[doc(hidden)] pub fn verify_extern_type, Id>() {} + +#[doc(hidden)] +pub fn verify_extern_kind, Kind>() {} diff --git a/src/lib.rs b/src/lib.rs index 9efac51..2f21de9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -395,6 +395,8 @@ mod unwind; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; +pub use crate::extern_type::kind::Opaque; +pub use crate::extern_type::kind::Trivial; pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; @@ -422,6 +424,7 @@ pub type Vector = CxxVector; #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; + pub use crate::extern_type::verify_extern_kind; pub use crate::extern_type::verify_extern_type; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; diff --git a/syntax/check.rs b/syntax/check.rs index 147984e..cfac236 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -338,6 +338,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { || cx.types.cxx.contains(ident) && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) + && !cx.types.required_trivial_aliases.contains(ident) || cx.types.rust.contains(ident) } @@ -376,7 +377,11 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } else if cx.types.enums.contains_key(ident) { "enum".to_owned() } else if cx.types.cxx.contains(ident) { - "C++ type".to_owned() + if cx.types.required_trivial_aliases.contains(ident) { + "trivial C++ type".to_owned() + } else { + "non-trivial C++ type".to_owned() + } } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { diff --git a/syntax/types.rs b/syntax/types.rs index 8a86a46..ff78268 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -14,6 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, + pub required_trivial_aliases: Set<&'a Ident>, } impl<'a> Types<'a> { @@ -135,6 +136,55 @@ impl<'a> Types<'a> { } } + // 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 mut required_trivial_aliases = Set::new(); + + fn insist_alias_types_are_trivial<'c>( + required_trivial_aliases: &mut Set<&'c Ident>, + aliases: &Map<&'c Ident, &'c TypeAlias>, + ty: &'c Type, + ) { + if let Type::Ident(ident) = ty { + if aliases.contains_key(ident) { + required_trivial_aliases.insert(ident); + } + } + } + + for api in apis { + match api { + Api::Struct(strct) => { + for field in &strct.fields { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &field.ty, + ); + } + } + Api::CxxFunction(efn) | Api::RustFunction(efn) => { + for arg in &efn.args { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &arg.ty, + ); + } + if let Some(ret) = &efn.ret { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &ret, + ); + } + } + _ => {} + } + } + Types { all, structs, @@ -143,6 +193,7 @@ impl<'a> Types<'a> { rust, aliases, untrusted, + required_trivial_aliases, } } From 405e8740a44110f59d720b0dab5847ab6db1b075 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Sep 30 2020 23:38:06 +0000 Subject: [PATCH 940/2232] Add C++ checks for triviality of Trivial types. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 862be81..5ea84fc 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -80,6 +80,11 @@ pub(super) fn gen( write_struct_with_methods(out, ety, methods); } } + Api::TypeAlias(ety) => { + if types.required_trivial_aliases.contains(&ety.ident) { + check_trivial_extern_type(out, &ety.ident) + } + } _ => {} } } @@ -124,13 +129,18 @@ pub(super) fn gen( fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) - | Some(I64) => out.include.cstdint = true, - Some(Usize) => out.include.cstddef = true, - Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} - }, + Type::Ident(ident) => { + match Atom::from(ident) { + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) + | Some(I32) | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, + Some(CxxString) => out.include.string = true, + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} + }; + if types.required_trivial_aliases.contains(&ident) { + out.include.type_traits = true; + }; + } Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, Type::CxxVector(_) => out.include.vector = true, @@ -401,6 +411,11 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } } +fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { + writeln!(out, "static_assert(std::is_trivially_move_constructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially move constructible in C++\");", id, id); + writeln!(out, "static_assert(std::is_trivially_destructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially destructible in C++\");", id, id); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { From d7f5bb902bf593c956f0f12922080be5fe3b50c8 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Sep 30 2020 23:38:06 +0000 Subject: [PATCH 941/2232] Documenting ExternType::Kind. --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 951bc6c..9bc5947 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -54,7 +54,7 @@ /// ## Integrating with bindgen-generated types /// /// Handwritten `ExternType` impls make it possible to plug in a data structure -/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX. +/// emitted by bindgen as the definition of a C++ type emitted by CXX. /// /// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ /// namespace and type name given in the type id refers to a C++ type that is @@ -69,10 +69,11 @@ /// # pub struct StringPiece([usize; 2]); /// # } /// -/// use cxx::{type_id, ExternType}; +/// use cxx::{type_id, ExternType, Opaque}; /// /// unsafe impl ExternType for folly_sys::StringPiece { /// type Id = type_id!("folly::StringPiece"); +/// type Kind = Opaque; /// } /// /// #[cxx::bridge(namespace = folly)] @@ -92,6 +93,29 @@ /// # /// # fn main() {} /// ``` +/// +/// ## Opaque and Trivial types +/// +/// Some C++ types are safe to hold and pass around in Rust, by value. +/// Those C++ types must have a trivial move constructor, and must +/// have no destructor. +/// +/// If you believe your C++ type is indeed trivial, you can specify +/// ``` +/// # struct TypeName; +/// # unsafe impl cxx::ExternType for TypeName { +/// type Id = cxx::type_id!("name::space::of::TypeName"); +/// type Kind = cxx::Trivial; +/// # } +/// ``` +/// which will enable you to pass it into C++ functions by value, +/// return it by value from such functions, and include it in +/// `struct`s that you have declared to `cxx::bridge`. Your promises +/// about the triviality of the C++ type will be checked using +/// `static_assert`s in the generated C++. +/// +/// Opaque types can't be passed by value, but can still be held +/// in `UniquePtr`. pub unsafe trait ExternType { /// A type-level representation of the type's C++ namespace and type name. /// @@ -101,12 +125,13 @@ pub unsafe trait ExternType { /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// type Kind = cxx::Opaque; /// # } /// ``` type Id; - /// Either `kind::Opaque` or `kind::Trivial`. If in doubt, use - /// `kind::Opaque`. + /// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use + /// `cxx::Opaque`. type Kind; } From feb0dc104c186887c3ded8778026bc0c61b2dec4 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Sep 30 2020 23:51:31 +0000 Subject: [PATCH 942/2232] Fix expected error messages. --- diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 1ff8dbf..e61ce84 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -1,4 +1,4 @@ -error: using C++ type by value is not supported +error: using non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:4:9 | 4 | c: C, @@ -16,13 +16,13 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: passing C++ type by value is not supported +error: passing non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:14 | 16 | fn f(c: C) -> C; | ^^^^ -error: returning C++ type by value is not supported +error: returning non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:23 | 16 | fn f(c: C) -> C; From 890083d64fc0ee2ccf298be549827e9c0a975628 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Oct 02 2020 18:36:33 +0000 Subject: [PATCH 943/2232] Preserve docs on aliases --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 955ad5f..c35044b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -657,9 +657,11 @@ fn expand_rust_function_shim_impl( } fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let doc = &alias.doc; let ident = &alias.ident; let ty = &alias.ty; quote! { + #doc pub type #ident = #ty; } } diff --git a/syntax/mod.rs b/syntax/mod.rs index f52a582..8ef3c8b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -79,6 +79,7 @@ pub struct ExternFn { } pub struct TypeAlias { + pub doc: Doc, pub type_token: Token![type], pub ident: Ident, pub eq_token: Token![=], diff --git a/syntax/parse.rs b/syntax/parse.rs index 3ac5304..c1c565f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -396,9 +396,10 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - attrs::parse_doc(cx, &attrs); + let doc = attrs::parse_doc(cx, &attrs); Ok(TypeAlias { + doc, type_token, ident, eq_token, From 4954ca171f57be0ddf0842e68ca0ce16cd03203d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 03 2020 23:38:22 +0000 Subject: [PATCH 944/2232] Merge pull request #332 from dtolnay/doc Preserve docs on aliases --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 955ad5f..c35044b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -657,9 +657,11 @@ fn expand_rust_function_shim_impl( } fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let doc = &alias.doc; let ident = &alias.ident; let ty = &alias.ty; quote! { + #doc pub type #ident = #ty; } } diff --git a/syntax/mod.rs b/syntax/mod.rs index f52a582..8ef3c8b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -79,6 +79,7 @@ pub struct ExternFn { } pub struct TypeAlias { + pub doc: Doc, pub type_token: Token![type], pub ident: Ident, pub eq_token: Token![=], diff --git a/syntax/parse.rs b/syntax/parse.rs index 3ac5304..c1c565f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -396,9 +396,10 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - attrs::parse_doc(cx, &attrs); + let doc = attrs::parse_doc(cx, &attrs); Ok(TypeAlias { + doc, type_token, ident, eq_token, From 9f69230783487f5bc2c6b9d9f44de2ddc2dc1146 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:53:30 +0000 Subject: [PATCH 945/2232] Merge pull request #325 from adetaylor/with-trivial-2 Allow type aliases to be marked as Trivial. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 2e2b81f..e0b1b30 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -80,6 +80,11 @@ pub(super) fn gen( write_struct_with_methods(out, ety, methods); } } + Api::TypeAlias(ety) => { + if types.required_trivial_aliases.contains(&ety.ident) { + check_trivial_extern_type(out, &ety.ident) + } + } _ => {} } } @@ -124,13 +129,18 @@ pub(super) fn gen( fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) - | Some(I64) => out.include.cstdint = true, - Some(Usize) => out.include.cstddef = true, - Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} - }, + Type::Ident(ident) => { + match Atom::from(ident) { + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) + | Some(I32) | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, + Some(CxxString) => out.include.string = true, + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} + }; + if types.required_trivial_aliases.contains(&ident) { + out.include.type_traits = true; + }; + } Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, Type::CxxVector(_) => out.include.vector = true, @@ -401,6 +411,11 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } } +fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { + writeln!(out, "static_assert(std::is_trivially_move_constructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially move constructible in C++\");", id, id); + writeln!(out, "static_assert(std::is_trivially_destructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially destructible in C++\");", id, id); +} + fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { let mut has_cxx_throws = false; for api in apis { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index c35044b..9c8a117 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -57,6 +57,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); hidden.extend(expand_type_alias_verify(namespace, alias)); + let ident = &alias.ident; + if types.required_trivial_aliases.contains(ident) { + hidden.extend(expand_type_alias_kind_trivial_verify(alias)); + } } } } @@ -179,6 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { unsafe impl ::cxx::ExternType for #ident { type Id = #type_id; + type Kind = ::cxx::Opaque; } } } @@ -679,6 +684,18 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt } } +fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream { + let ident = &type_alias.ident; + let begin_span = type_alias.type_token.span; + let end_span = type_alias.semi_token.span; + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); + let end = quote_spanned!(end_span=> >); + + quote! { + const _: fn() = #begin #ident, ::cxx::Trivial #end; + } +} + fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { let mut path = String::new(); for name in namespace { diff --git a/src/extern_type.rs b/src/extern_type.rs index 6701ef5..9bc5947 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -54,7 +54,7 @@ /// ## Integrating with bindgen-generated types /// /// Handwritten `ExternType` impls make it possible to plug in a data structure -/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX. +/// emitted by bindgen as the definition of a C++ type emitted by CXX. /// /// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ /// namespace and type name given in the type id refers to a C++ type that is @@ -69,10 +69,11 @@ /// # pub struct StringPiece([usize; 2]); /// # } /// -/// use cxx::{type_id, ExternType}; +/// use cxx::{type_id, ExternType, Opaque}; /// /// unsafe impl ExternType for folly_sys::StringPiece { /// type Id = type_id!("folly::StringPiece"); +/// type Kind = Opaque; /// } /// /// #[cxx::bridge(namespace = folly)] @@ -92,6 +93,29 @@ /// # /// # fn main() {} /// ``` +/// +/// ## Opaque and Trivial types +/// +/// Some C++ types are safe to hold and pass around in Rust, by value. +/// Those C++ types must have a trivial move constructor, and must +/// have no destructor. +/// +/// If you believe your C++ type is indeed trivial, you can specify +/// ``` +/// # struct TypeName; +/// # unsafe impl cxx::ExternType for TypeName { +/// type Id = cxx::type_id!("name::space::of::TypeName"); +/// type Kind = cxx::Trivial; +/// # } +/// ``` +/// which will enable you to pass it into C++ functions by value, +/// return it by value from such functions, and include it in +/// `struct`s that you have declared to `cxx::bridge`. Your promises +/// about the triviality of the C++ type will be checked using +/// `static_assert`s in the generated C++. +/// +/// Opaque types can't be passed by value, but can still be held +/// in `UniquePtr`. pub unsafe trait ExternType { /// A type-level representation of the type's C++ namespace and type name. /// @@ -101,10 +125,32 @@ pub unsafe trait ExternType { /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// type Kind = cxx::Opaque; /// # } /// ``` type Id; + + /// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use + /// `cxx::Opaque`. + type Kind; +} + +pub(crate) mod kind { + + /// An opaque type which can't be passed or held by value within Rust. + /// For example, a C++ type with a destructor, or a non-trivial move + /// constructor. Rust's strict move semantics mean that we can't own + /// these by value in Rust, but they can still be owned by a + /// `UniquePtr`... + pub struct Opaque; + + /// A type with trivial move constructors and no destructor, which + /// can therefore be owned and moved around in Rust code directly. + pub struct Trivial; } #[doc(hidden)] pub fn verify_extern_type, Id>() {} + +#[doc(hidden)] +pub fn verify_extern_kind, Kind>() {} diff --git a/src/lib.rs b/src/lib.rs index 5f52f49..b53a7b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -395,6 +395,8 @@ mod unwind; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; +pub use crate::extern_type::kind::Opaque; +pub use crate::extern_type::kind::Trivial; pub use crate::extern_type::ExternType; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; @@ -422,6 +424,7 @@ pub type Vector = CxxVector; #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; + pub use crate::extern_type::verify_extern_kind; pub use crate::extern_type::verify_extern_type; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; diff --git a/syntax/check.rs b/syntax/check.rs index 147984e..cfac236 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -338,6 +338,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { || cx.types.cxx.contains(ident) && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) + && !cx.types.required_trivial_aliases.contains(ident) || cx.types.rust.contains(ident) } @@ -376,7 +377,11 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } else if cx.types.enums.contains_key(ident) { "enum".to_owned() } else if cx.types.cxx.contains(ident) { - "C++ type".to_owned() + if cx.types.required_trivial_aliases.contains(ident) { + "trivial C++ type".to_owned() + } else { + "non-trivial C++ type".to_owned() + } } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { diff --git a/syntax/types.rs b/syntax/types.rs index 8a86a46..ff78268 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -14,6 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, + pub required_trivial_aliases: Set<&'a Ident>, } impl<'a> Types<'a> { @@ -135,6 +136,55 @@ impl<'a> Types<'a> { } } + // 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 mut required_trivial_aliases = Set::new(); + + fn insist_alias_types_are_trivial<'c>( + required_trivial_aliases: &mut Set<&'c Ident>, + aliases: &Map<&'c Ident, &'c TypeAlias>, + ty: &'c Type, + ) { + if let Type::Ident(ident) = ty { + if aliases.contains_key(ident) { + required_trivial_aliases.insert(ident); + } + } + } + + for api in apis { + match api { + Api::Struct(strct) => { + for field in &strct.fields { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &field.ty, + ); + } + } + Api::CxxFunction(efn) | Api::RustFunction(efn) => { + for arg in &efn.args { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &arg.ty, + ); + } + if let Some(ret) = &efn.ret { + insist_alias_types_are_trivial( + &mut required_trivial_aliases, + &aliases, + &ret, + ); + } + } + _ => {} + } + } + Types { all, structs, @@ -143,6 +193,7 @@ impl<'a> Types<'a> { rust, aliases, untrusted, + required_trivial_aliases, } } diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 1ff8dbf..e61ce84 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -1,4 +1,4 @@ -error: using C++ type by value is not supported +error: using non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:4:9 | 4 | c: C, @@ -16,13 +16,13 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: passing C++ type by value is not supported +error: passing non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:14 | 16 | fn f(c: C) -> C; | ^^^^ -error: returning C++ type by value is not supported +error: returning non-trivial C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:23 | 16 | fn f(c: C) -> C; From 38f5ad69da170b57bbacc77d0f669863c0055396 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:13 +0000 Subject: [PATCH 946/2232] Move Opaque and Trivial marker types under a ::kind module In the interest of keeping the root module focused on the most widely used items only. --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9c8a117..fb011d5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -183,7 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { unsafe impl ::cxx::ExternType for #ident { type Id = #type_id; - type Kind = ::cxx::Opaque; + type Kind = ::cxx::kind::Opaque; } } } @@ -692,7 +692,7 @@ fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream let end = quote_spanned!(end_span=> >); quote! { - const _: fn() = #begin #ident, ::cxx::Trivial #end; + const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; } } diff --git a/src/extern_type.rs b/src/extern_type.rs index 9bc5947..d35b690 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -69,11 +69,11 @@ /// # pub struct StringPiece([usize; 2]); /// # } /// -/// use cxx::{type_id, ExternType, Opaque}; +/// use cxx::{type_id, ExternType}; /// /// unsafe impl ExternType for folly_sys::StringPiece { /// type Id = type_id!("folly::StringPiece"); -/// type Kind = Opaque; +/// type Kind = cxx::kind::Opaque; /// } /// /// #[cxx::bridge(namespace = folly)] @@ -105,7 +105,7 @@ /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); -/// type Kind = cxx::Trivial; +/// type Kind = cxx::kind::Trivial; /// # } /// ``` /// which will enable you to pass it into C++ functions by value, @@ -125,18 +125,17 @@ pub unsafe trait ExternType { /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); - /// type Kind = cxx::Opaque; + /// type Kind = cxx::kind::Opaque; /// # } /// ``` type Id; - /// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use - /// `cxx::Opaque`. + /// Either `cxx::kind::Opaque` or `cxx::kind::Trivial`. If in doubt, use + /// `cxx::kind::Opaque`. type Kind; } -pub(crate) mod kind { - +pub mod kind { /// An opaque type which can't be passed or held by value within Rust. /// For example, a C++ type with a destructor, or a non-trivial move /// constructor. Rust's strict move semantics mean that we can't own diff --git a/src/lib.rs b/src/lib.rs index b53a7b6..815eacf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -395,9 +395,7 @@ mod unwind; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; -pub use crate::extern_type::kind::Opaque; -pub use crate::extern_type::kind::Trivial; -pub use crate::extern_type::ExternType; +pub use crate::extern_type::{kind, ExternType}; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; From 1bd502d89379471d41e10483de3f32d5cd4c0fa9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:13 +0000 Subject: [PATCH 947/2232] Add documentation of cxx::kind module --- diff --git a/src/extern_type.rs b/src/extern_type.rs index d35b690..498a558 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -94,6 +94,8 @@ /// # fn main() {} /// ``` /// +///

+/// /// ## Opaque and Trivial types /// /// Some C++ types are safe to hold and pass around in Rust, by value. @@ -135,6 +137,13 @@ pub unsafe trait ExternType { type Kind; } +/// Marker types identifying Rust's knowledge about an extern C++ type. +/// +/// These markers are used in the `Kind` associated type in impls of the +/// [`ExternType`] trait. Refer to the discussion of [Opaque and Trivial +/// types][trait] for an overview of their purpose. +/// +/// [trait]: ExternType#opaque-and-trivial-types pub mod kind { /// An opaque type which can't be passed or held by value within Rust. /// For example, a C++ type with a destructor, or a non-trivial move From 978401978a64c7e0dbd855bb0195da851604cf82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 948/2232] Update documentation of Opaque and Trivial markers --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 498a558..d5a5d67 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -145,15 +145,21 @@ pub unsafe trait ExternType { /// /// [trait]: ExternType#opaque-and-trivial-types pub mod kind { - /// An opaque type which can't be passed or held by value within Rust. - /// For example, a C++ type with a destructor, or a non-trivial move - /// constructor. Rust's strict move semantics mean that we can't own - /// these by value in Rust, but they can still be owned by a - /// `UniquePtr`... + /// An opaque type which cannot be passed or held by value within Rust. + /// + /// Rust's move semantics are such that every move is equivalent to a + /// memcpy. This is incompatible in general with C++'s constructor-based + /// move semantics, so a C++ type which has a destructor or nontrivial move + /// constructor must never exist by value in Rust. In CXX, such types are + /// called opaque C++ types. + /// + /// When passed across an FFI boundary, an opaque C++ type must be behind an + /// indirection such as a reference or UniquePtr. pub struct Opaque; - /// A type with trivial move constructors and no destructor, which - /// can therefore be owned and moved around in Rust code directly. + /// A type with trivial move constructor and no destructor, which can + /// therefore be owned and moved around in Rust code without requiring + /// indirection. pub struct Trivial; } From 4e8c7b9fc6c1e4197bc3b4d7ed07a1a31a9409c3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 949/2232] Move overview of extern type kinds to the Kind associated type --- diff --git a/src/extern_type.rs b/src/extern_type.rs index d5a5d67..8737d8b 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -93,31 +93,6 @@ /// # /// # fn main() {} /// ``` -/// -///

-/// -/// ## Opaque and Trivial types -/// -/// Some C++ types are safe to hold and pass around in Rust, by value. -/// Those C++ types must have a trivial move constructor, and must -/// have no destructor. -/// -/// If you believe your C++ type is indeed trivial, you can specify -/// ``` -/// # struct TypeName; -/// # unsafe impl cxx::ExternType for TypeName { -/// type Id = cxx::type_id!("name::space::of::TypeName"); -/// type Kind = cxx::kind::Trivial; -/// # } -/// ``` -/// which will enable you to pass it into C++ functions by value, -/// return it by value from such functions, and include it in -/// `struct`s that you have declared to `cxx::bridge`. Your promises -/// about the triviality of the C++ type will be checked using -/// `static_assert`s in the generated C++. -/// -/// Opaque types can't be passed by value, but can still be held -/// in `UniquePtr`. pub unsafe trait ExternType { /// A type-level representation of the type's C++ namespace and type name. /// @@ -134,16 +109,35 @@ pub unsafe trait ExternType { /// Either `cxx::kind::Opaque` or `cxx::kind::Trivial`. If in doubt, use /// `cxx::kind::Opaque`. + /// + /// Some C++ types are safe to hold and pass around in Rust, by value. + /// Those C++ types must have a trivial move constructor, and must + /// have no destructor. + /// + /// If you believe your C++ type is indeed trivial, you can specify + /// ``` + /// # struct TypeName; + /// # unsafe impl cxx::ExternType for TypeName { + /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// type Kind = cxx::kind::Trivial; + /// # } + /// ``` + /// which will enable you to pass it into C++ functions by value, + /// return it by value from such functions, and include it in + /// `struct`s that you have declared to `cxx::bridge`. Your promises + /// about the triviality of the C++ type will be checked using + /// `static_assert`s in the generated C++. + /// + /// Opaque types can't be passed by value, but can still be held + /// in `UniquePtr`. type Kind; } /// Marker types identifying Rust's knowledge about an extern C++ type. /// -/// These markers are used in the `Kind` associated type in impls of the -/// [`ExternType`] trait. Refer to the discussion of [Opaque and Trivial -/// types][trait] for an overview of their purpose. -/// -/// [trait]: ExternType#opaque-and-trivial-types +/// These markers are used in the [`Kind`][ExternType::Kind] associated type in +/// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an +/// overview of their purpose. pub mod kind { /// An opaque type which cannot be passed or held by value within Rust. /// From 0d161d2c18f6b0258db2aa947669fe724d0567d8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 950/2232] Show only the associated type being discussed --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 8737d8b..b1400ad 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -102,7 +102,7 @@ pub unsafe trait ExternType { /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); - /// type Kind = cxx::kind::Opaque; + /// # type Kind = cxx::kind::Opaque; /// # } /// ``` type Id; @@ -118,7 +118,7 @@ pub unsafe trait ExternType { /// ``` /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { - /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// # type Id = cxx::type_id!("name::space::of::TypeName"); /// type Kind = cxx::kind::Trivial; /// # } /// ``` From 73e0582300296aab2d543304511ea5b2a724f393 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 951/2232] Update documentation of ExternType::Kind --- diff --git a/src/extern_type.rs b/src/extern_type.rs index b1400ad..554c406 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -107,14 +107,22 @@ pub unsafe trait ExternType { /// ``` type Id; - /// Either `cxx::kind::Opaque` or `cxx::kind::Trivial`. If in doubt, use - /// `cxx::kind::Opaque`. + /// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`]. /// - /// Some C++ types are safe to hold and pass around in Rust, by value. - /// Those C++ types must have a trivial move constructor, and must - /// have no destructor. + /// [`cxx::kind::Opaque`]: kind::Opaque + /// [`cxx::kind::Trivial`]: kind::Trivial + /// + /// A C++ type is only okay to hold and pass around by value in Rust if its + /// [move constructor is trivial] and it has no destructor. In CXX, these + /// are called Trivial extern C++ types, while types with nontrivial move + /// behavior or a destructor must be considered Opaque and handled by Rust + /// only behind an indirection, such as a reference or UniquePtr. + /// + /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible + /// + /// If you believe your C++ type reflected by this ExternType impl is indeed + /// trivial, you can specify: /// - /// If you believe your C++ type is indeed trivial, you can specify /// ``` /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { @@ -122,14 +130,11 @@ pub unsafe trait ExternType { /// type Kind = cxx::kind::Trivial; /// # } /// ``` - /// which will enable you to pass it into C++ functions by value, - /// return it by value from such functions, and include it in - /// `struct`s that you have declared to `cxx::bridge`. Your promises - /// about the triviality of the C++ type will be checked using - /// `static_assert`s in the generated C++. /// - /// Opaque types can't be passed by value, but can still be held - /// in `UniquePtr`. + /// which will enable you to pass it into C++ functions by value, return it + /// by value, and include it in `struct`s that you have declared to + /// `cxx::bridge`. Your claim about the triviality of the C++ type will be + /// checked by a `static_assert` in the generated C++ side of the binding. type Kind; } From 61ca18da6fa20b3a4bdd0549c3b915de6a5dabe8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 952/2232] Make kind::Opaque and kind::Trivial impossible to instantiate These marker types are never intended to exist at runtime, only at compile time in a trait impl. --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 554c406..ab7b3fd 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -154,12 +154,12 @@ pub mod kind { /// /// When passed across an FFI boundary, an opaque C++ type must be behind an /// indirection such as a reference or UniquePtr. - pub struct Opaque; + pub enum Opaque {} /// A type with trivial move constructor and no destructor, which can /// therefore be owned and moved around in Rust code without requiring /// indirection. - pub struct Trivial; + pub enum Trivial {} } #[doc(hidden)] From 43232b9effde329dbfae09316495ddbafd458253 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 953/2232] Add trait bound to ExternType::Kind --- diff --git a/src/extern_type.rs b/src/extern_type.rs index ab7b3fd..b984f55 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,3 +1,5 @@ +use self::kind::Kind; + /// A type for which the layout is determined by its C++ definition. /// /// This trait serves the following two related purposes. @@ -135,7 +137,7 @@ pub unsafe trait ExternType { /// by value, and include it in `struct`s that you have declared to /// `cxx::bridge`. Your claim about the triviality of the C++ type will be /// checked by a `static_assert` in the generated C++ side of the binding. - type Kind; + type Kind: Kind; } /// Marker types identifying Rust's knowledge about an extern C++ type. @@ -160,6 +162,10 @@ pub mod kind { /// therefore be owned and moved around in Rust code without requiring /// indirection. pub enum Trivial {} + + pub trait Kind {} + impl Kind for Opaque {} + impl Kind for Trivial {} } #[doc(hidden)] From 38670b9a700e77bafa35137167a558c3ddb9d8d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 03:54:14 +0000 Subject: [PATCH 954/2232] Seal the Kind trait This trait is not intended to be implementable outside of the cxx crate, and by sealing we retain the ability to add methods or associated items into the trait in the future without a breaking change. --- diff --git a/src/extern_type.rs b/src/extern_type.rs index b984f55..0432096 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,4 +1,4 @@ -use self::kind::Kind; +use self::kind::{Kind, Opaque, Trivial}; /// A type for which the layout is determined by its C++ definition. /// @@ -146,6 +146,8 @@ pub unsafe trait ExternType { /// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an /// overview of their purpose. pub mod kind { + use super::private; + /// An opaque type which cannot be passed or held by value within Rust. /// /// Rust's move semantics are such that every move is equivalent to a @@ -163,13 +165,19 @@ pub mod kind { /// indirection. pub enum Trivial {} - pub trait Kind {} + pub trait Kind: private::Sealed {} impl Kind for Opaque {} impl Kind for Trivial {} } +mod private { + pub trait Sealed {} + impl Sealed for super::Opaque {} + impl Sealed for super::Trivial {} +} + #[doc(hidden)] pub fn verify_extern_type, Id>() {} #[doc(hidden)] -pub fn verify_extern_kind, Kind>() {} +pub fn verify_extern_kind, Kind: self::Kind>() {} From 445dcc6a33aad1e724ee6664c1d46ffc83ff66e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:02:17 +0000 Subject: [PATCH 955/2232] Merge pull request #333 from dtolnay/kind Update documentation and API of Trivial and Opaque kind markers --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 9c8a117..fb011d5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -183,7 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { unsafe impl ::cxx::ExternType for #ident { type Id = #type_id; - type Kind = ::cxx::Opaque; + type Kind = ::cxx::kind::Opaque; } } } @@ -692,7 +692,7 @@ fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream let end = quote_spanned!(end_span=> >); quote! { - const _: fn() = #begin #ident, ::cxx::Trivial #end; + const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; } } diff --git a/src/extern_type.rs b/src/extern_type.rs index 9bc5947..0432096 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,3 +1,5 @@ +use self::kind::{Kind, Opaque, Trivial}; + /// A type for which the layout is determined by its C++ definition. /// /// This trait serves the following two related purposes. @@ -69,11 +71,11 @@ /// # pub struct StringPiece([usize; 2]); /// # } /// -/// use cxx::{type_id, ExternType, Opaque}; +/// use cxx::{type_id, ExternType}; /// /// unsafe impl ExternType for folly_sys::StringPiece { /// type Id = type_id!("folly::StringPiece"); -/// type Kind = Opaque; +/// type Kind = cxx::kind::Opaque; /// } /// /// #[cxx::bridge(namespace = folly)] @@ -93,29 +95,6 @@ /// # /// # fn main() {} /// ``` -/// -/// ## Opaque and Trivial types -/// -/// Some C++ types are safe to hold and pass around in Rust, by value. -/// Those C++ types must have a trivial move constructor, and must -/// have no destructor. -/// -/// If you believe your C++ type is indeed trivial, you can specify -/// ``` -/// # struct TypeName; -/// # unsafe impl cxx::ExternType for TypeName { -/// type Id = cxx::type_id!("name::space::of::TypeName"); -/// type Kind = cxx::Trivial; -/// # } -/// ``` -/// which will enable you to pass it into C++ functions by value, -/// return it by value from such functions, and include it in -/// `struct`s that you have declared to `cxx::bridge`. Your promises -/// about the triviality of the C++ type will be checked using -/// `static_assert`s in the generated C++. -/// -/// Opaque types can't be passed by value, but can still be held -/// in `UniquePtr`. pub unsafe trait ExternType { /// A type-level representation of the type's C++ namespace and type name. /// @@ -125,32 +104,80 @@ pub unsafe trait ExternType { /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { /// type Id = cxx::type_id!("name::space::of::TypeName"); - /// type Kind = cxx::Opaque; + /// # type Kind = cxx::kind::Opaque; /// # } /// ``` type Id; - /// Either `cxx::Opaque` or `cxx::Trivial`. If in doubt, use - /// `cxx::Opaque`. - type Kind; + /// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`]. + /// + /// [`cxx::kind::Opaque`]: kind::Opaque + /// [`cxx::kind::Trivial`]: kind::Trivial + /// + /// A C++ type is only okay to hold and pass around by value in Rust if its + /// [move constructor is trivial] and it has no destructor. In CXX, these + /// are called Trivial extern C++ types, while types with nontrivial move + /// behavior or a destructor must be considered Opaque and handled by Rust + /// only behind an indirection, such as a reference or UniquePtr. + /// + /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible + /// + /// If you believe your C++ type reflected by this ExternType impl is indeed + /// trivial, you can specify: + /// + /// ``` + /// # struct TypeName; + /// # unsafe impl cxx::ExternType for TypeName { + /// # type Id = cxx::type_id!("name::space::of::TypeName"); + /// type Kind = cxx::kind::Trivial; + /// # } + /// ``` + /// + /// which will enable you to pass it into C++ functions by value, return it + /// by value, and include it in `struct`s that you have declared to + /// `cxx::bridge`. Your claim about the triviality of the C++ type will be + /// checked by a `static_assert` in the generated C++ side of the binding. + type Kind: Kind; } -pub(crate) mod kind { +/// Marker types identifying Rust's knowledge about an extern C++ type. +/// +/// These markers are used in the [`Kind`][ExternType::Kind] associated type in +/// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an +/// overview of their purpose. +pub mod kind { + use super::private; + + /// An opaque type which cannot be passed or held by value within Rust. + /// + /// Rust's move semantics are such that every move is equivalent to a + /// memcpy. This is incompatible in general with C++'s constructor-based + /// move semantics, so a C++ type which has a destructor or nontrivial move + /// constructor must never exist by value in Rust. In CXX, such types are + /// called opaque C++ types. + /// + /// When passed across an FFI boundary, an opaque C++ type must be behind an + /// indirection such as a reference or UniquePtr. + pub enum Opaque {} + + /// A type with trivial move constructor and no destructor, which can + /// therefore be owned and moved around in Rust code without requiring + /// indirection. + pub enum Trivial {} - /// An opaque type which can't be passed or held by value within Rust. - /// For example, a C++ type with a destructor, or a non-trivial move - /// constructor. Rust's strict move semantics mean that we can't own - /// these by value in Rust, but they can still be owned by a - /// `UniquePtr`... - pub struct Opaque; + pub trait Kind: private::Sealed {} + impl Kind for Opaque {} + impl Kind for Trivial {} +} - /// A type with trivial move constructors and no destructor, which - /// can therefore be owned and moved around in Rust code directly. - pub struct Trivial; +mod private { + pub trait Sealed {} + impl Sealed for super::Opaque {} + impl Sealed for super::Trivial {} } #[doc(hidden)] pub fn verify_extern_type, Id>() {} #[doc(hidden)] -pub fn verify_extern_kind, Kind>() {} +pub fn verify_extern_kind, Kind: self::Kind>() {} diff --git a/src/lib.rs b/src/lib.rs index b53a7b6..815eacf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -395,9 +395,7 @@ mod unwind; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; -pub use crate::extern_type::kind::Opaque; -pub use crate::extern_type::kind::Trivial; -pub use crate::extern_type::ExternType; +pub use crate::extern_type::{kind, ExternType}; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; From 63a0e4ee03a3288aa3a246b973952459ae4688fd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:02:27 +0000 Subject: [PATCH 956/2232] Combine Kind verification into expand_type_alias_verify --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fb011d5..02817a4 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -56,11 +56,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias)); - let ident = &alias.ident; - if types.required_trivial_aliases.contains(ident) { - hidden.extend(expand_type_alias_kind_trivial_verify(alias)); - } + hidden.extend(expand_type_alias_verify(namespace, alias, types)); } } } @@ -671,7 +667,11 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { +fn expand_type_alias_verify( + namespace: &Namespace, + alias: &TypeAlias, + types: &Types, +) -> TokenStream { let ident = &alias.ident; let type_id = type_id(namespace, ident); let begin_span = alias.type_token.span; @@ -679,21 +679,18 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); let end = quote_spanned!(end_span=> >); - quote! { + let mut verify = quote! { const _: fn() = #begin #ident, #type_id #end; - } -} - -fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream { - let ident = &type_alias.ident; - let begin_span = type_alias.type_token.span; - let end_span = type_alias.semi_token.span; - let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); - let end = quote_spanned!(end_span=> >); + }; - quote! { - const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; + if types.required_trivial_aliases.contains(&alias.ident) { + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); + verify.extend(quote! { + const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; + }); } + + verify } fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { diff --git a/src/lib.rs b/src/lib.rs index 815eacf..9287101 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,8 +422,7 @@ pub type Vector = CxxVector; #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; - pub use crate::extern_type::verify_extern_kind; - pub use crate::extern_type::verify_extern_type; + pub use crate::extern_type::{verify_extern_kind, verify_extern_type}; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; From 163564c305bf25ce67310080579bc661840474cd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:02:27 +0000 Subject: [PATCH 957/2232] Simplify scan for required trivial aliases using a closure --- diff --git a/syntax/types.rs b/syntax/types.rs index ff78268..8ecc417 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -141,44 +141,26 @@ impl<'a> Types<'a> { // the APIs above, in case some function or struct references a type // which is declared subsequently. let mut required_trivial_aliases = Set::new(); - - fn insist_alias_types_are_trivial<'c>( - required_trivial_aliases: &mut Set<&'c Ident>, - aliases: &Map<&'c Ident, &'c TypeAlias>, - ty: &'c Type, - ) { + let mut insist_alias_types_are_trivial = |ty: &'a Type| { if let Type::Ident(ident) = ty { if aliases.contains_key(ident) { required_trivial_aliases.insert(ident); } } - } - + }; for api in apis { match api { Api::Struct(strct) => { for field in &strct.fields { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &field.ty, - ); + insist_alias_types_are_trivial(&field.ty); } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { for arg in &efn.args { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &arg.ty, - ); + insist_alias_types_are_trivial(&arg.ty); } if let Some(ret) = &efn.ret { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &ret, - ); + insist_alias_types_are_trivial(&ret); } } _ => {} From 89e386d115393de3d72ac3732ece486736e0bea5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:07:48 +0000 Subject: [PATCH 958/2232] Atom cannot be in required_trivial_aliases --- diff --git a/gen/src/write.rs b/gen/src/write.rs index e0b1b30..e9e2239 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -129,18 +129,18 @@ pub(super) fn gen( fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => { - match Atom::from(ident) { - Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) - | Some(I32) | Some(I64) => out.include.cstdint = true, - Some(Usize) => out.include.cstddef = true, - Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} - }; - if types.required_trivial_aliases.contains(&ident) { - out.include.type_traits = true; - }; - } + Type::Ident(ident) => match Atom::from(ident) { + Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) + | Some(I64) => out.include.cstdint = true, + Some(Usize) => out.include.cstddef = true, + Some(CxxString) => out.include.string = true, + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => {} + None => { + if types.required_trivial_aliases.contains(&ident) { + out.include.type_traits = true; + } + } + }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, Type::CxxVector(_) => out.include.vector = true, From 7426cc1c33a5ac3e9721c4daa10e74d288561378 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:07:57 +0000 Subject: [PATCH 959/2232] Wrap the trivial type static assertions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index e9e2239..ddde8a9 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -412,8 +412,24 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { - writeln!(out, "static_assert(std::is_trivially_move_constructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially move constructible in C++\");", id, id); - writeln!(out, "static_assert(std::is_trivially_destructible<{}>::value,\"type {} marked as Trivial in Rust is not trivially destructible in C++\");", id, id); + writeln!(out, "static_assert("); + writeln!( + out, + " std::is_trivially_move_constructible<{}>::value,", + id, + ); + writeln!( + out, + " \"type {} marked as Trivial in Rust is not trivially move constructible in C++\");", + id, + ); + writeln!(out, "static_assert("); + writeln!(out, " std::is_trivially_destructible<{}>::value,", id); + writeln!( + out, + " \"type {} marked as Trivial in Rust is not trivially destructible in C++\");", + id, + ); } fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { From 373d70457a8e9907173beb5f410f6e7d53f8e7ca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:09:11 +0000 Subject: [PATCH 960/2232] Merge pull request #334 from dtolnay/kind Touch up PR 325 --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fb011d5..02817a4 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -56,11 +56,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias)); - let ident = &alias.ident; - if types.required_trivial_aliases.contains(ident) { - hidden.extend(expand_type_alias_kind_trivial_verify(alias)); - } + hidden.extend(expand_type_alias_verify(namespace, alias, types)); } } } @@ -671,7 +667,11 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { +fn expand_type_alias_verify( + namespace: &Namespace, + alias: &TypeAlias, + types: &Types, +) -> TokenStream { let ident = &alias.ident; let type_id = type_id(namespace, ident); let begin_span = alias.type_token.span; @@ -679,21 +679,18 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); let end = quote_spanned!(end_span=> >); - quote! { + let mut verify = quote! { const _: fn() = #begin #ident, #type_id #end; - } -} - -fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream { - let ident = &type_alias.ident; - let begin_span = type_alias.type_token.span; - let end_span = type_alias.semi_token.span; - let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); - let end = quote_spanned!(end_span=> >); + }; - quote! { - const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; + if types.required_trivial_aliases.contains(&alias.ident) { + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); + verify.extend(quote! { + const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; + }); } + + verify } fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { diff --git a/src/lib.rs b/src/lib.rs index 815eacf..9287101 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,8 +422,7 @@ pub type Vector = CxxVector; #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; - pub use crate::extern_type::verify_extern_kind; - pub use crate::extern_type::verify_extern_type; + pub use crate::extern_type::{verify_extern_kind, verify_extern_type}; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; diff --git a/syntax/types.rs b/syntax/types.rs index ff78268..8ecc417 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -141,44 +141,26 @@ impl<'a> Types<'a> { // the APIs above, in case some function or struct references a type // which is declared subsequently. let mut required_trivial_aliases = Set::new(); - - fn insist_alias_types_are_trivial<'c>( - required_trivial_aliases: &mut Set<&'c Ident>, - aliases: &Map<&'c Ident, &'c TypeAlias>, - ty: &'c Type, - ) { + let mut insist_alias_types_are_trivial = |ty: &'a Type| { if let Type::Ident(ident) = ty { if aliases.contains_key(ident) { required_trivial_aliases.insert(ident); } } - } - + }; for api in apis { match api { Api::Struct(strct) => { for field in &strct.fields { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &field.ty, - ); + insist_alias_types_are_trivial(&field.ty); } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { for arg in &efn.args { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &arg.ty, - ); + insist_alias_types_are_trivial(&arg.ty); } if let Some(ret) = &efn.ret { - insist_alias_types_are_trivial( - &mut required_trivial_aliases, - &aliases, - &ret, - ); + insist_alias_types_are_trivial(&ret); } } _ => {} From 11fd6d0f1bd377ab6f3a774a6cb4a564b0a35f88 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:09:23 +0000 Subject: [PATCH 961/2232] An unordered set suffices for required_trivial_aliases --- diff --git a/syntax/types.rs b/syntax/types.rs index 8ecc417..7eb22d4 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -14,7 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial_aliases: Set<&'a Ident>, + pub required_trivial_aliases: UnorderedSet<&'a Ident>, } impl<'a> Types<'a> { @@ -140,7 +140,7 @@ impl<'a> Types<'a> { // 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 mut required_trivial_aliases = Set::new(); + let mut required_trivial_aliases = UnorderedSet::new(); let mut insist_alias_types_are_trivial = |ty: &'a Type| { if let Type::Ident(ident) = ty { if aliases.contains_key(ident) { From b5aca7b8a7ee7b88534d39657beefabd8bed9a01 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:10:02 +0000 Subject: [PATCH 962/2232] Collect reason that each alias is required trivial --- diff --git a/gen/src/write.rs b/gen/src/write.rs index ddde8a9..1f4b7bc 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -81,7 +81,7 @@ pub(super) fn gen( } } Api::TypeAlias(ety) => { - if types.required_trivial_aliases.contains(&ety.ident) { + if types.required_trivial_aliases.contains_key(&ety.ident) { check_trivial_extern_type(out, &ety.ident) } } @@ -136,7 +136,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(CxxString) => out.include.string = true, Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => {} None => { - if types.required_trivial_aliases.contains(&ident) { + if types.required_trivial_aliases.contains_key(&ident) { out.include.type_traits = true; } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 02817a4..739529f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -683,7 +683,7 @@ fn expand_type_alias_verify( const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial_aliases.contains(&alias.ident) { + if types.required_trivial_aliases.contains_key(&alias.ident) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; diff --git a/syntax/check.rs b/syntax/check.rs index cfac236..1543918 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -338,7 +338,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { || cx.types.cxx.contains(ident) && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) - && !cx.types.required_trivial_aliases.contains(ident) + && !cx.types.required_trivial_aliases.contains_key(ident) || cx.types.rust.contains(ident) } @@ -377,7 +377,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String { } else if cx.types.enums.contains_key(ident) { "enum".to_owned() } else if cx.types.cxx.contains(ident) { - if cx.types.required_trivial_aliases.contains(ident) { + if cx.types.required_trivial_aliases.contains_key(ident) { "trivial C++ type".to_owned() } else { "non-trivial C++ type".to_owned() diff --git a/syntax/types.rs b/syntax/types.rs index 7eb22d4..efccb2b 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type, TypeAlias}; +use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -14,7 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial_aliases: UnorderedSet<&'a Ident>, + pub required_trivial_aliases: Map<&'a Ident, TrivialReason<'a>>, } impl<'a> Types<'a> { @@ -140,27 +140,30 @@ impl<'a> Types<'a> { // 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 mut required_trivial_aliases = UnorderedSet::new(); - let mut insist_alias_types_are_trivial = |ty: &'a Type| { + let mut required_trivial_aliases = Map::new(); + let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| { if let Type::Ident(ident) = ty { if aliases.contains_key(ident) { - required_trivial_aliases.insert(ident); + required_trivial_aliases.entry(ident).or_insert(reason); } } }; for api in apis { match api { Api::Struct(strct) => { + let reason = TrivialReason::StructField(strct); for field in &strct.fields { - insist_alias_types_are_trivial(&field.ty); + insist_alias_types_are_trivial(&field.ty, reason); } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { + let reason = TrivialReason::FunctionArgument(efn); for arg in &efn.args { - insist_alias_types_are_trivial(&arg.ty); + insist_alias_types_are_trivial(&arg.ty, reason); } if let Some(ret) = &efn.ret { - insist_alias_types_are_trivial(&ret); + let reason = TrivialReason::FunctionReturn(efn); + insist_alias_types_are_trivial(&ret, reason); } } _ => {} @@ -211,6 +214,13 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } } +#[derive(Copy, Clone)] +pub enum TrivialReason<'a> { + StructField(&'a Struct), + FunctionArgument(&'a ExternFn), + FunctionReturn(&'a ExternFn), +} + 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); From 3208fd7a582bee72046b821dae191bac2026bcf0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:10:05 +0000 Subject: [PATCH 963/2232] Provide more helpful error when opaque C++ type used by value --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 1f4b7bc..3c81dc8 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -81,7 +81,7 @@ pub(super) fn gen( } } Api::TypeAlias(ety) => { - if types.required_trivial_aliases.contains_key(&ety.ident) { + if types.required_trivial.contains_key(&ety.ident) { check_trivial_extern_type(out, &ety.ident) } } @@ -136,7 +136,9 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(CxxString) => out.include.string = true, Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => {} None => { - if types.required_trivial_aliases.contains_key(&ident) { + if types.aliases.contains_key(ident) + && types.required_trivial.contains_key(ident) + { out.include.type_traits = true; } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 739529f..e7b28b9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -683,7 +683,7 @@ fn expand_type_alias_verify( const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial_aliases.contains_key(&alias.ident) { + if types.required_trivial.contains_key(&alias.ident) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; diff --git a/syntax/check.rs b/syntax/check.rs index 1543918..4960111 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; +use crate::syntax::types::TrivialReason; use crate::syntax::{ error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, @@ -208,6 +209,19 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { fn check_api_type(cx: &mut Check, ty: &ExternType) { check_reserved_name(cx, &ty.ident); + + if let Some(reason) = cx.types.required_trivial.get(&ty.ident) { + let what = match reason { + TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident), + TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident), + }; + let msg = format!( + "needs a cxx::ExternType impl in order to be used as {}", + what, + ); + cx.error(ty, msg); + } } fn check_api_fn(cx: &mut Check, efn: &ExternFn) { @@ -338,7 +352,8 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { || cx.types.cxx.contains(ident) && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) - && !cx.types.required_trivial_aliases.contains_key(ident) + && !(cx.types.aliases.contains_key(ident) + && cx.types.required_trivial.contains_key(ident)) || cx.types.rust.contains(ident) } @@ -376,12 +391,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String { "struct".to_owned() } else if cx.types.enums.contains_key(ident) { "enum".to_owned() + } else if cx.types.aliases.contains_key(ident) { + "C++ type".to_owned() } else if cx.types.cxx.contains(ident) { - if cx.types.required_trivial_aliases.contains_key(ident) { - "trivial C++ type".to_owned() - } else { - "non-trivial C++ type".to_owned() - } + "opaque C++ type".to_owned() } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { diff --git a/syntax/types.rs b/syntax/types.rs index efccb2b..6924a78 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -14,7 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial_aliases: Map<&'a Ident, TrivialReason<'a>>, + pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, } impl<'a> Types<'a> { @@ -140,11 +140,11 @@ impl<'a> Types<'a> { // 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 mut required_trivial_aliases = Map::new(); + let mut required_trivial = Map::new(); let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| { if let Type::Ident(ident) = ty { - if aliases.contains_key(ident) { - required_trivial_aliases.entry(ident).or_insert(reason); + if cxx.contains(ident) { + required_trivial.entry(ident).or_insert(reason); } } }; @@ -178,7 +178,7 @@ impl<'a> Types<'a> { rust, aliases, untrusted, - required_trivial_aliases, + required_trivial, } } diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index e61ce84..0a56dd4 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -1,4 +1,4 @@ -error: using non-trivial C++ type by value is not supported +error: using opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:4:9 | 4 | c: C, @@ -16,13 +16,19 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: passing non-trivial C++ type by value is not supported +error: needs a cxx::ExternType impl in order to be used as a field of `S` + --> $DIR/by_value_not_supported.rs:10:9 + | +10 | type C; + | ^^^^^^ + +error: passing opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:14 | 16 | fn f(c: C) -> C; | ^^^^ -error: returning non-trivial C++ type by value is not supported +error: returning opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:23 | 16 | fn f(c: C) -> C; From 44198dd15a306922f61da07e08f5aa6492e40a0e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:19:37 +0000 Subject: [PATCH 964/2232] Merge pull request #335 from dtolnay/kind Add dedicated error message referring to cxx::ExternType when opaque type is used by value --- diff --git a/gen/src/write.rs b/gen/src/write.rs index ddde8a9..3c81dc8 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -81,7 +81,7 @@ pub(super) fn gen( } } Api::TypeAlias(ety) => { - if types.required_trivial_aliases.contains(&ety.ident) { + if types.required_trivial.contains_key(&ety.ident) { check_trivial_extern_type(out, &ety.ident) } } @@ -136,7 +136,9 @@ fn write_includes(out: &mut OutFile, types: &Types) { Some(CxxString) => out.include.string = true, Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => {} None => { - if types.required_trivial_aliases.contains(&ident) { + if types.aliases.contains_key(ident) + && types.required_trivial.contains_key(ident) + { out.include.type_traits = true; } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 02817a4..e7b28b9 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -683,7 +683,7 @@ fn expand_type_alias_verify( const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial_aliases.contains(&alias.ident) { + if types.required_trivial.contains_key(&alias.ident) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; diff --git a/syntax/check.rs b/syntax/check.rs index cfac236..4960111 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; +use crate::syntax::types::TrivialReason; use crate::syntax::{ error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types, @@ -208,6 +209,19 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { fn check_api_type(cx: &mut Check, ty: &ExternType) { check_reserved_name(cx, &ty.ident); + + if let Some(reason) = cx.types.required_trivial.get(&ty.ident) { + let what = match reason { + TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident), + TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident), + }; + let msg = format!( + "needs a cxx::ExternType impl in order to be used as {}", + what, + ); + cx.error(ty, msg); + } } fn check_api_fn(cx: &mut Check, efn: &ExternFn) { @@ -338,7 +352,8 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool { || cx.types.cxx.contains(ident) && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) - && !cx.types.required_trivial_aliases.contains(ident) + && !(cx.types.aliases.contains_key(ident) + && cx.types.required_trivial.contains_key(ident)) || cx.types.rust.contains(ident) } @@ -376,12 +391,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String { "struct".to_owned() } else if cx.types.enums.contains_key(ident) { "enum".to_owned() + } else if cx.types.aliases.contains_key(ident) { + "C++ type".to_owned() } else if cx.types.cxx.contains(ident) { - if cx.types.required_trivial_aliases.contains(ident) { - "trivial C++ type".to_owned() - } else { - "non-trivial C++ type".to_owned() - } + "opaque C++ type".to_owned() } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() } else if Atom::from(ident) == Some(CxxString) { diff --git a/syntax/types.rs b/syntax/types.rs index 8ecc417..6924a78 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type, TypeAlias}; +use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -14,7 +14,7 @@ pub struct Types<'a> { pub rust: Set<&'a Ident>, pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial_aliases: Set<&'a Ident>, + pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, } impl<'a> Types<'a> { @@ -140,27 +140,30 @@ impl<'a> Types<'a> { // 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 mut required_trivial_aliases = Set::new(); - let mut insist_alias_types_are_trivial = |ty: &'a Type| { + let mut required_trivial = Map::new(); + let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| { if let Type::Ident(ident) = ty { - if aliases.contains_key(ident) { - required_trivial_aliases.insert(ident); + if cxx.contains(ident) { + required_trivial.entry(ident).or_insert(reason); } } }; for api in apis { match api { Api::Struct(strct) => { + let reason = TrivialReason::StructField(strct); for field in &strct.fields { - insist_alias_types_are_trivial(&field.ty); + insist_alias_types_are_trivial(&field.ty, reason); } } Api::CxxFunction(efn) | Api::RustFunction(efn) => { + let reason = TrivialReason::FunctionArgument(efn); for arg in &efn.args { - insist_alias_types_are_trivial(&arg.ty); + insist_alias_types_are_trivial(&arg.ty, reason); } if let Some(ret) = &efn.ret { - insist_alias_types_are_trivial(&ret); + let reason = TrivialReason::FunctionReturn(efn); + insist_alias_types_are_trivial(&ret, reason); } } _ => {} @@ -175,7 +178,7 @@ impl<'a> Types<'a> { rust, aliases, untrusted, - required_trivial_aliases, + required_trivial, } } @@ -211,6 +214,13 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> { } } +#[derive(Copy, Clone)] +pub enum TrivialReason<'a> { + StructField(&'a Struct), + FunctionArgument(&'a ExternFn), + FunctionReturn(&'a ExternFn), +} + 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); diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index e61ce84..0a56dd4 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -1,4 +1,4 @@ -error: using non-trivial C++ type by value is not supported +error: using opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:4:9 | 4 | c: C, @@ -16,13 +16,19 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: passing non-trivial C++ type by value is not supported +error: needs a cxx::ExternType impl in order to be used as a field of `S` + --> $DIR/by_value_not_supported.rs:10:9 + | +10 | type C; + | ^^^^^^ + +error: passing opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:14 | 16 | fn f(c: C) -> C; | ^^^^ -error: returning non-trivial C++ type by value is not supported +error: returning opaque C++ type by value is not supported --> $DIR/by_value_not_supported.rs:16:23 | 16 | fn f(c: C) -> C; From 599fad85bf78d3b3421a4d8f833e618932afd1ca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:21:32 +0000 Subject: [PATCH 965/2232] Debug impl for OrderedSet --- diff --git a/syntax/set.rs b/syntax/set.rs index de13088..b553169 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; +use std::fmt::{self, Debug}; use std::hash::Hash; use std::slice; @@ -47,3 +48,12 @@ impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> { self.0.next().copied() } } + +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() + } +} From 4cefa728e050bf342550997450c8564bddb16975 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:24:07 +0000 Subject: [PATCH 966/2232] Consistently use ety for extern type variables --- diff --git a/syntax/check.rs b/syntax/check.rs index 4960111..f256948 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -46,7 +46,7 @@ fn do_typecheck(cx: &mut Check) { match api { Api::Struct(strct) => check_api_struct(cx, strct), Api::Enum(enm) => check_api_enum(cx, enm), - Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty), + Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety), Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn), _ => {} } @@ -207,10 +207,10 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } } -fn check_api_type(cx: &mut Check, ty: &ExternType) { - check_reserved_name(cx, &ty.ident); +fn check_api_type(cx: &mut Check, ety: &ExternType) { + check_reserved_name(cx, &ety.ident); - if let Some(reason) = cx.types.required_trivial.get(&ty.ident) { + if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { let what = match reason { TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident), @@ -220,7 +220,7 @@ fn check_api_type(cx: &mut Check, ty: &ExternType) { "needs a cxx::ExternType impl in order to be used as {}", what, ); - cx.error(ty, msg); + cx.error(ety, msg); } } diff --git a/syntax/parse.rs b/syntax/parse.rs index c1c565f..cec818d 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -221,7 +221,7 @@ fn parse_foreign_mod( } let mut types = items.iter().filter_map(|item| match item { - Api::CxxType(ty) | Api::RustType(ty) => Some(&ty.ident), + Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident), Api::TypeAlias(alias) => Some(&alias.ident), _ => None, }); From fabca77bf8a0cfe9f2b945dd1e351aa214263b0f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 04:25:41 +0000 Subject: [PATCH 967/2232] Move type alias trivial asserts to section --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3c81dc8..4276e9b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -80,15 +80,19 @@ pub(super) fn gen( write_struct_with_methods(out, ety, methods); } } - Api::TypeAlias(ety) => { - if types.required_trivial.contains_key(&ety.ident) { - check_trivial_extern_type(out, &ety.ident) - } - } _ => {} } } + out.next_section(); + for api in apis { + if let Api::TypeAlias(ety) = api { + if types.required_trivial.contains_key(&ety.ident) { + check_trivial_extern_type(out, &ety.ident) + } + } + } + if !header { out.begin_block("extern \"C\""); write_exception_glue(out, apis); From a291281acc7c105b9dcf358ef6762fb75f578201 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 06:23:02 +0000 Subject: [PATCH 968/2232] Add explanatory readmes to .devcontainer and .vscode dirs --- diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..b30aebc --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,4 @@ +This directory contains the container setup used when developing CXX inside of +GitHub [Codespaces]. + +[Codespaces]: https://github.com/features/codespaces diff --git a/.vscode/README.md b/.vscode/README.md new file mode 100644 index 0000000..5ed5b27 --- /dev/null +++ b/.vscode/README.md @@ -0,0 +1,4 @@ +VS Code actions and configuration. Applicable when developing CXX inside of +GitHub [Codespaces]. + +[Codespaces]: https://github.com/features/codespaces From 762f0419749520a363c5c266923529d507d9f8b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 06:24:42 +0000 Subject: [PATCH 969/2232] Fix behavior of --include and --output flags It was not intended that these would consume multiple arguments after a single instance of the flag. --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index 152f11a..bd3bcfd 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -136,6 +136,7 @@ into the generated C++ code as #include lines. .short("i") .takes_value(true) .multiple(true) + .number_of_values(1) .validator_os(validate_utf8) .help(HELP) } @@ -150,6 +151,7 @@ not specified. .short("o") .takes_value(true) .multiple(true) + .number_of_values(1) .validator_os(validate_utf8) .help(HELP) } From c86633d53c1150048cbcb2605403ffb227c853b4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 06:48:58 +0000 Subject: [PATCH 970/2232] Generalize OrderedSet::contains method --- diff --git a/syntax/set.rs b/syntax/set.rs index b553169..891df60 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::collections::HashSet; use std::fmt::{self, Debug}; use std::hash::Hash; @@ -27,7 +28,11 @@ where new } - pub fn contains(&self, value: &T) -> bool { + pub fn contains(&self, value: &Q) -> bool + where + &'a T: Borrow, + Q: ?Sized + Hash + Eq, + { self.set.contains(value) } } From 7e69f89714717d7f6ca8ea3d2056035d860fd6cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:20:17 +0000 Subject: [PATCH 971/2232] Explicitly requesting an instantiation --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 4276e9b..b83611a 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1056,14 +1056,18 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + if Atom::from(inner).is_none() + && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + { out.next_section(); write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + if Atom::from(inner).is_none() + && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + { out.next_section(); write_cxx_vector(out, ty, inner, types); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e7b28b9..4778b14 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { - Api::Include(_) | Api::RustType(_) => {} + Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { @@ -76,13 +76,17 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { + if Atom::from(ident).is_none() + && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + { expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { + if Atom::from(ident).is_none() + && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. diff --git a/syntax/check.rs b/syntax/check.rs index f256948..ff23ec9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -3,8 +3,8 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ - error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, - Types, + error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1, + Type, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; @@ -48,6 +48,7 @@ fn do_typecheck(cx: &mut Check) { 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::Impl(imp) => check_api_impl(cx, imp), _ => {} } } @@ -286,6 +287,18 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { check_multiple_arg_lifetimes(cx, efn); } +fn check_api_impl(cx: &mut Check, imp: &Impl) { + if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + return; + } + } + } + + cx.error(imp, "unsupported Self type of explicit impl"); +} + fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(ty)) if ty.mutability.is_some() => {} diff --git a/syntax/file.rs b/syntax/file.rs index 8b86adc..931ce6e 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -2,8 +2,8 @@ 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, ItemStruct, - ItemUse, LitStr, Token, Visibility, + braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl, + ItemStruct, ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -22,6 +22,7 @@ pub enum Item { Enum(ItemEnum), ForeignMod(ItemForeignMod), Use(ItemUse), + Impl(ItemImpl), Other(RustItem), } @@ -99,6 +100,7 @@ impl Parse for Item { brace_token: item.brace_token, items: item.items, })), + RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })), RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), other => Ok(Item::Other(other)), } diff --git a/syntax/ident.rs b/syntax/ident.rs index 7545e92..74e7799 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { for api in apis { match api { - Api::Include(_) => {} + Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { check(cx, &strct.ident); for field in &strct.fields { diff --git a/syntax/mod.rs b/syntax/mod.rs index 8ef3c8b..934f6c6 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -42,6 +42,7 @@ pub enum Api { RustType(ExternType), RustFunction(ExternFn), TypeAlias(TypeAlias), + Impl(Impl), } pub struct ExternType { @@ -87,6 +88,12 @@ pub struct TypeAlias { pub semi_token: Token![;], } +pub struct Impl { + pub impl_token: Token![impl], + pub ty: Type, + pub brace_token: Brace, +} + pub struct Signature { pub unsafety: Option, pub fn_token: Token![fn], diff --git a/syntax/parse.rs b/syntax/parse.rs index cec818d..49dc26b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,17 +3,17 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, - Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -33,6 +33,10 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec Err(err) => cx.push(err), }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), + Item::Impl(item) => match parse_impl(item) { + Ok(imp) => apis.push(imp), + Err(err) => cx.push(err), + }, Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), Item::Other(item) => cx.error(item, "unsupported item"), } @@ -420,6 +424,37 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } +fn parse_impl(imp: ItemImpl) -> Result { + if !imp.items.is_empty() { + let mut span = Group::new(Delimiter::Brace, TokenStream::new()); + span.set_span(imp.brace_token.span); + return Err(Error::new_spanned(span, "expected an empty impl block")); + } + + let self_ty = &imp.self_ty; + if let Some((bang, path, for_token)) = &imp.trait_ { + let span = quote!(#bang #path #for_token #self_ty); + return Err(Error::new_spanned( + span, + "unexpected impl, expected something like `impl UniquePtr {}`", + )); + } + + let generics = &imp.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + return Err(Error::new_spanned( + imp, + "generic parameters on an impl is not supported", + )); + } + + Ok(Api::Impl(Impl { + impl_token: imp.impl_token, + ty: parse_type(&self_ty)?, + brace_token: imp.brace_token, + })) +} + fn parse_include(input: ParseStream) -> Result { if input.peek(LitStr) { return Ok(input.parse::()?.value()); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 4ed264a..7618e99 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, - TypeAlias, Var, + Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, Signature, Slice, Struct, Ty1, + Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -121,6 +121,14 @@ impl ToTokens for ExternFn { } } +impl ToTokens for Impl { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.impl_token.to_tokens(tokens); + self.ty.to_tokens(tokens); + self.brace_token.surround(tokens, |_tokens| {}); + } +} + impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { self.fn_token.to_tokens(tokens); diff --git a/syntax/types.rs b/syntax/types.rs index 6924a78..2afce25 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -15,6 +15,7 @@ pub struct Types<'a> { pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, + pub explicit_impls: Set<&'a Type>, } impl<'a> Types<'a> { @@ -26,6 +27,7 @@ impl<'a> Types<'a> { let mut rust = Set::new(); let mut aliases = Map::new(); let mut untrusted = Map::new(); + let mut explicit_impls = Set::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -133,6 +135,10 @@ impl<'a> Types<'a> { cxx.insert(ident); aliases.insert(ident, alias); } + Api::Impl(imp) => { + visit(&mut all, &imp.ty); + explicit_impls.insert(&imp.ty); + } } } @@ -179,6 +185,7 @@ impl<'a> Types<'a> { aliases, untrusted, required_trivial, + explicit_impls, } } From ac8394bd1897a1f1880238a4fecc5cf5f45d21c0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:20:21 +0000 Subject: [PATCH 972/2232] Add test of UniquePtrTarget impl conflict --- diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs new file mode 100644 index 0000000..b6cb4d4 --- /dev/null +++ b/tests/ui/unique_ptr_twice.rs @@ -0,0 +1,19 @@ +#[cxx::bridge] +mod here { + extern "C" { + type C; + } + + impl UniquePtr {} +} + +#[cxx::bridge] +mod there { + extern "C" { + type C = crate::here::C; + } + + impl UniquePtr {} +} + +fn main() {} diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr new file mode 100644 index 0000000..d77d3ff --- /dev/null +++ b/tests/ui/unique_ptr_twice.stderr @@ -0,0 +1,10 @@ +error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`: + --> $DIR/unique_ptr_twice.rs:10:1 + | +1 | #[cxx::bridge] + | -------------- first implementation here +... +10 | #[cxx::bridge] + | ^^^^^^^^^^^^^^ conflicting implementation for `here::C` + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From 50b7563de4e41dace6477d707d33fa5bd4141d85 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:20:22 +0000 Subject: [PATCH 973/2232] Add ui test of explicit impl with unsupported Self --- diff --git a/tests/ui/bad_explicit_impl.rs b/tests/ui/bad_explicit_impl.rs new file mode 100644 index 0000000..2106446 --- /dev/null +++ b/tests/ui/bad_explicit_impl.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl fn() -> &S {} +} + +fn main() {} diff --git a/tests/ui/bad_explicit_impl.stderr b/tests/ui/bad_explicit_impl.stderr new file mode 100644 index 0000000..cd0a317 --- /dev/null +++ b/tests/ui/bad_explicit_impl.stderr @@ -0,0 +1,5 @@ +error: unsupported Self type of explicit impl + --> $DIR/bad_explicit_impl.rs:7:5 + | +7 | impl fn() -> &S {} + | ^^^^^^^^^^^^^^^^^^ From 2b3c2b2fa4ac49ddd382d3dded3fdb9008c4162c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:20:22 +0000 Subject: [PATCH 974/2232] Add ui test of invalid nonempty impl block --- diff --git a/tests/ui/nonempty_impl_block.rs b/tests/ui/nonempty_impl_block.rs new file mode 100644 index 0000000..239d1ec --- /dev/null +++ b/tests/ui/nonempty_impl_block.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl UniquePtr { + fn new() -> Self; + } +} + +fn main() {} diff --git a/tests/ui/nonempty_impl_block.stderr b/tests/ui/nonempty_impl_block.stderr new file mode 100644 index 0000000..e7881bb --- /dev/null +++ b/tests/ui/nonempty_impl_block.stderr @@ -0,0 +1,8 @@ +error: expected an empty impl block + --> $DIR/nonempty_impl_block.rs:7:23 + | +7 | impl UniquePtr { + | _______________________^ +8 | | fn new() -> Self; +9 | | } + | |_____^ From 64cab486888bc24fc7e4abbaef6d12f74960f0cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:20:22 +0000 Subject: [PATCH 975/2232] Add ui test of explicit impl trait for type --- diff --git a/tests/ui/impl_trait_for_type.rs b/tests/ui/impl_trait_for_type.rs new file mode 100644 index 0000000..9284f73 --- /dev/null +++ b/tests/ui/impl_trait_for_type.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl UniquePtrTarget for S {} +} + +fn main() {} diff --git a/tests/ui/impl_trait_for_type.stderr b/tests/ui/impl_trait_for_type.stderr new file mode 100644 index 0000000..e05a461 --- /dev/null +++ b/tests/ui/impl_trait_for_type.stderr @@ -0,0 +1,5 @@ +error: unexpected impl, expected something like `impl UniquePtr {}` + --> $DIR/impl_trait_for_type.rs:7:10 + | +7 | impl UniquePtrTarget for S {} + | ^^^^^^^^^^^^^^^^^^^^^ From 1cbd1b29ddc853fa4d2a965a9ee879d041fe3a83 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:35:35 +0000 Subject: [PATCH 976/2232] Merge pull request #336 from dtolnay/impl Explicitly requesting an instantiation --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 4276e9b..b83611a 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1056,14 +1056,18 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + if Atom::from(inner).is_none() + && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + { out.next_section(); write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + if Atom::from(inner).is_none() + && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + { out.next_section(); write_cxx_vector(out, ty, inner, types); } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e7b28b9..4778b14 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { - Api::Include(_) | Api::RustType(_) => {} + Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { @@ -76,13 +76,17 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { + if Atom::from(ident).is_none() + && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + { expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { + if Atom::from(ident).is_none() + && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. diff --git a/syntax/check.rs b/syntax/check.rs index f256948..ff23ec9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -3,8 +3,8 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ - error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, - Types, + error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1, + Type, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; @@ -48,6 +48,7 @@ fn do_typecheck(cx: &mut Check) { 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::Impl(imp) => check_api_impl(cx, imp), _ => {} } } @@ -286,6 +287,18 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { check_multiple_arg_lifetimes(cx, efn); } +fn check_api_impl(cx: &mut Check, imp: &Impl) { + if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { + if let Type::Ident(inner) = &ty.inner { + if Atom::from(inner).is_none() { + return; + } + } + } + + cx.error(imp, "unsupported Self type of explicit impl"); +} + fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) { match &efn.ret { Some(Type::Ref(ty)) if ty.mutability.is_some() => {} diff --git a/syntax/file.rs b/syntax/file.rs index 8b86adc..931ce6e 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -2,8 +2,8 @@ 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, ItemStruct, - ItemUse, LitStr, Token, Visibility, + braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl, + ItemStruct, ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -22,6 +22,7 @@ pub enum Item { Enum(ItemEnum), ForeignMod(ItemForeignMod), Use(ItemUse), + Impl(ItemImpl), Other(RustItem), } @@ -99,6 +100,7 @@ impl Parse for Item { brace_token: item.brace_token, items: item.items, })), + RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })), RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), other => Ok(Item::Other(other)), } diff --git a/syntax/ident.rs b/syntax/ident.rs index 7545e92..74e7799 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { for api in apis { match api { - Api::Include(_) => {} + Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { check(cx, &strct.ident); for field in &strct.fields { diff --git a/syntax/mod.rs b/syntax/mod.rs index 8ef3c8b..934f6c6 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -42,6 +42,7 @@ pub enum Api { RustType(ExternType), RustFunction(ExternFn), TypeAlias(TypeAlias), + Impl(Impl), } pub struct ExternType { @@ -87,6 +88,12 @@ pub struct TypeAlias { pub semi_token: Token![;], } +pub struct Impl { + pub impl_token: Token![impl], + pub ty: Type, + pub brace_token: Brace, +} + pub struct Signature { pub unsafety: Option, pub fn_token: Token![fn], diff --git a/syntax/parse.rs b/syntax/parse.rs index cec818d..49dc26b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,17 +3,17 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, - Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -33,6 +33,10 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec Err(err) => cx.push(err), }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), + Item::Impl(item) => match parse_impl(item) { + Ok(imp) => apis.push(imp), + Err(err) => cx.push(err), + }, Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), Item::Other(item) => cx.error(item, "unsupported item"), } @@ -420,6 +424,37 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } +fn parse_impl(imp: ItemImpl) -> Result { + if !imp.items.is_empty() { + let mut span = Group::new(Delimiter::Brace, TokenStream::new()); + span.set_span(imp.brace_token.span); + return Err(Error::new_spanned(span, "expected an empty impl block")); + } + + let self_ty = &imp.self_ty; + if let Some((bang, path, for_token)) = &imp.trait_ { + let span = quote!(#bang #path #for_token #self_ty); + return Err(Error::new_spanned( + span, + "unexpected impl, expected something like `impl UniquePtr {}`", + )); + } + + let generics = &imp.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + return Err(Error::new_spanned( + imp, + "generic parameters on an impl is not supported", + )); + } + + Ok(Api::Impl(Impl { + impl_token: imp.impl_token, + ty: parse_type(&self_ty)?, + brace_token: imp.brace_token, + })) +} + fn parse_include(input: ParseStream) -> Result { if input.peek(LitStr) { return Ok(input.parse::()?.value()); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 4ed264a..7618e99 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, - TypeAlias, Var, + Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, Signature, Slice, Struct, Ty1, + Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -121,6 +121,14 @@ impl ToTokens for ExternFn { } } +impl ToTokens for Impl { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.impl_token.to_tokens(tokens); + self.ty.to_tokens(tokens); + self.brace_token.surround(tokens, |_tokens| {}); + } +} + impl ToTokens for Signature { fn to_tokens(&self, tokens: &mut TokenStream) { self.fn_token.to_tokens(tokens); diff --git a/syntax/types.rs b/syntax/types.rs index 6924a78..2afce25 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -15,6 +15,7 @@ pub struct Types<'a> { pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, + pub explicit_impls: Set<&'a Type>, } impl<'a> Types<'a> { @@ -26,6 +27,7 @@ impl<'a> Types<'a> { let mut rust = Set::new(); let mut aliases = Map::new(); let mut untrusted = Map::new(); + let mut explicit_impls = Set::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -133,6 +135,10 @@ impl<'a> Types<'a> { cxx.insert(ident); aliases.insert(ident, alias); } + Api::Impl(imp) => { + visit(&mut all, &imp.ty); + explicit_impls.insert(&imp.ty); + } } } @@ -179,6 +185,7 @@ impl<'a> Types<'a> { aliases, untrusted, required_trivial, + explicit_impls, } } diff --git a/tests/ui/bad_explicit_impl.rs b/tests/ui/bad_explicit_impl.rs new file mode 100644 index 0000000..2106446 --- /dev/null +++ b/tests/ui/bad_explicit_impl.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl fn() -> &S {} +} + +fn main() {} diff --git a/tests/ui/bad_explicit_impl.stderr b/tests/ui/bad_explicit_impl.stderr new file mode 100644 index 0000000..cd0a317 --- /dev/null +++ b/tests/ui/bad_explicit_impl.stderr @@ -0,0 +1,5 @@ +error: unsupported Self type of explicit impl + --> $DIR/bad_explicit_impl.rs:7:5 + | +7 | impl fn() -> &S {} + | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl_trait_for_type.rs b/tests/ui/impl_trait_for_type.rs new file mode 100644 index 0000000..9284f73 --- /dev/null +++ b/tests/ui/impl_trait_for_type.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl UniquePtrTarget for S {} +} + +fn main() {} diff --git a/tests/ui/impl_trait_for_type.stderr b/tests/ui/impl_trait_for_type.stderr new file mode 100644 index 0000000..e05a461 --- /dev/null +++ b/tests/ui/impl_trait_for_type.stderr @@ -0,0 +1,5 @@ +error: unexpected impl, expected something like `impl UniquePtr {}` + --> $DIR/impl_trait_for_type.rs:7:10 + | +7 | impl UniquePtrTarget for S {} + | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/nonempty_impl_block.rs b/tests/ui/nonempty_impl_block.rs new file mode 100644 index 0000000..239d1ec --- /dev/null +++ b/tests/ui/nonempty_impl_block.rs @@ -0,0 +1,12 @@ +#[cxx::bridge] +mod ffi { + struct S { + x: u8, + } + + impl UniquePtr { + fn new() -> Self; + } +} + +fn main() {} diff --git a/tests/ui/nonempty_impl_block.stderr b/tests/ui/nonempty_impl_block.stderr new file mode 100644 index 0000000..e7881bb --- /dev/null +++ b/tests/ui/nonempty_impl_block.stderr @@ -0,0 +1,8 @@ +error: expected an empty impl block + --> $DIR/nonempty_impl_block.rs:7:23 + | +7 | impl UniquePtr { + | _______________________^ +8 | | fn new() -> Self; +9 | | } + | |_____^ diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs new file mode 100644 index 0000000..b6cb4d4 --- /dev/null +++ b/tests/ui/unique_ptr_twice.rs @@ -0,0 +1,19 @@ +#[cxx::bridge] +mod here { + extern "C" { + type C; + } + + impl UniquePtr {} +} + +#[cxx::bridge] +mod there { + extern "C" { + type C = crate::here::C; + } + + impl UniquePtr {} +} + +fn main() {} diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr new file mode 100644 index 0000000..d77d3ff --- /dev/null +++ b/tests/ui/unique_ptr_twice.stderr @@ -0,0 +1,10 @@ +error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`: + --> $DIR/unique_ptr_twice.rs:10:1 + | +1 | #[cxx::bridge] + | -------------- first implementation here +... +10 | #[cxx::bridge] + | ^^^^^^^^^^^^^^ conflicting implementation for `here::C` + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From 6586c69a114a274481cbab1017927d6273419b4d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:36:47 +0000 Subject: [PATCH 977/2232] Store original Impl in explicit impls set --- diff --git a/syntax/impls.rs b/syntax/impls.rs index ebebb3e..6a177d5 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,5 @@ -use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ExternFn, Impl, Receiver, Ref, Signature, Slice, Ty1, Type}; +use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; @@ -238,3 +239,38 @@ impl Hash for Receiver { ty.hash(state); } } + +impl Hash for Impl { + fn hash(&self, state: &mut H) { + let Impl { + impl_token: _, + ty, + brace_token: _, + } = self; + ty.hash(state); + } +} + +impl Eq for Impl {} + +impl PartialEq for Impl { + fn eq(&self, other: &Impl) -> bool { + let Impl { + impl_token: _, + ty, + brace_token: _, + } = self; + let Impl { + impl_token: _, + ty: ty2, + brace_token: _, + } = other; + ty == ty2 + } +} + +impl Borrow for &Impl { + fn borrow(&self) -> &Type { + &self.ty + } +} diff --git a/syntax/types.rs b/syntax/types.rs index 2afce25..3f8d10c 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type, TypeAlias}; +use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -15,7 +15,7 @@ pub struct Types<'a> { pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, - pub explicit_impls: Set<&'a Type>, + pub explicit_impls: Set<&'a Impl>, } impl<'a> Types<'a> { @@ -137,7 +137,7 @@ impl<'a> Types<'a> { } Api::Impl(imp) => { visit(&mut all, &imp.ty); - explicit_impls.insert(&imp.ty); + explicit_impls.insert(imp); } } } From 0531f43ce9c777b8e579a87f0411bf9b328158ce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:36:56 +0000 Subject: [PATCH 978/2232] Improve error message on a conflicting explicit impl --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4778b14..13134d0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -4,9 +4,10 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, + Types, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::{parse_quote, Result, Token}; @@ -62,6 +63,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } for ty in types { + let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { if Atom::from(ident).is_none() { @@ -77,20 +79,20 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() - && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { - expanded.extend(expand_unique_ptr(namespace, ident, types)); + expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() - && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident)); + expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); } } } @@ -784,7 +786,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { +fn expand_unique_ptr( + namespace: &Namespace, + ident: &Ident, + types: &Types, + explicit_impl: Option<&Impl>, +) -> TokenStream { let name = ident.to_string(); let prefix = format!("cxxbridge04$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); @@ -810,8 +817,13 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok None }; - quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #ident { + let begin_span = + explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); + let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span); + let unsafe_token = format_ident!("unsafe", span = begin_span); + + quote_spanned! {end_span=> + #unsafe_token impl ::cxx::private::UniquePtrTarget for #ident { const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { @@ -857,7 +869,12 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok } } -fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { +fn expand_cxx_vector( + namespace: &Namespace, + elem: &Ident, + explicit_impl: Option<&Impl>, +) -> TokenStream { + let _ = explicit_impl; let name = elem.to_string(); let prefix = format!("cxxbridge04$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); @@ -869,8 +886,13 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); - quote! { - unsafe impl ::cxx::private::VectorElement for #elem { + let begin_span = + explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); + let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span); + let unsafe_token = format_ident!("unsafe", span = begin_span); + + quote_spanned! {end_span=> + #unsafe_token impl ::cxx::private::VectorElement for #elem { const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __vector_size(v: &::cxx::CxxVector) -> usize { extern "C" { diff --git a/syntax/set.rs b/syntax/set.rs index 891df60..73e3909 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -35,6 +35,14 @@ where { self.set.contains(value) } + + pub fn get(&self, value: &Q) -> Option<&'a T> + where + &'a T: Borrow, + Q: ?Sized + Hash + Eq, + { + self.set.get(value).copied() + } } impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> { diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr index d77d3ff..5686cf1 100644 --- a/tests/ui/unique_ptr_twice.stderr +++ b/tests/ui/unique_ptr_twice.stderr @@ -1,10 +1,8 @@ error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`: - --> $DIR/unique_ptr_twice.rs:10:1 + --> $DIR/unique_ptr_twice.rs:16:5 | -1 | #[cxx::bridge] - | -------------- first implementation here +7 | impl UniquePtr {} + | ----------------- first implementation here ... -10 | #[cxx::bridge] - | ^^^^^^^^^^^^^^ conflicting implementation for `here::C` - | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +16 | impl UniquePtr {} + | ^^^^^^^^^^^^^^^^^ conflicting implementation for `here::C` From debd9d5bf325adbbf070434a8d6acfb390c04948 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 07:48:22 +0000 Subject: [PATCH 979/2232] Merge pull request #337 from dtolnay/impl Improve error message on a conflicting explicit impl --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4778b14..13134d0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -4,9 +4,10 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, + Types, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::{parse_quote, Result, Token}; @@ -62,6 +63,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } for ty in types { + let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { if Atom::from(ident).is_none() { @@ -77,20 +79,20 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() - && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { - expanded.extend(expand_unique_ptr(namespace, ident, types)); + expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() - && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty)) + && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident)); + expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); } } } @@ -784,7 +786,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { } } -fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream { +fn expand_unique_ptr( + namespace: &Namespace, + ident: &Ident, + types: &Types, + explicit_impl: Option<&Impl>, +) -> TokenStream { let name = ident.to_string(); let prefix = format!("cxxbridge04$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); @@ -810,8 +817,13 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok None }; - quote! { - unsafe impl ::cxx::private::UniquePtrTarget for #ident { + let begin_span = + explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); + let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span); + let unsafe_token = format_ident!("unsafe", span = begin_span); + + quote_spanned! {end_span=> + #unsafe_token impl ::cxx::private::UniquePtrTarget for #ident { const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __null() -> *mut ::std::ffi::c_void { extern "C" { @@ -857,7 +869,12 @@ fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> Tok } } -fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { +fn expand_cxx_vector( + namespace: &Namespace, + elem: &Ident, + explicit_impl: Option<&Impl>, +) -> TokenStream { + let _ = explicit_impl; let name = elem.to_string(); let prefix = format!("cxxbridge04$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); @@ -869,8 +886,13 @@ fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream { let link_unique_ptr_release = format!("{}release", unique_ptr_prefix); let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix); - quote! { - unsafe impl ::cxx::private::VectorElement for #elem { + let begin_span = + explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); + let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span); + let unsafe_token = format_ident!("unsafe", span = begin_span); + + quote_spanned! {end_span=> + #unsafe_token impl ::cxx::private::VectorElement for #elem { const __NAME: &'static dyn ::std::fmt::Display = &#name; fn __vector_size(v: &::cxx::CxxVector) -> usize { extern "C" { diff --git a/syntax/impls.rs b/syntax/impls.rs index ebebb3e..6a177d5 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,4 +1,5 @@ -use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ExternFn, Impl, Receiver, Ref, Signature, Slice, Ty1, Type}; +use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; @@ -238,3 +239,38 @@ impl Hash for Receiver { ty.hash(state); } } + +impl Hash for Impl { + fn hash(&self, state: &mut H) { + let Impl { + impl_token: _, + ty, + brace_token: _, + } = self; + ty.hash(state); + } +} + +impl Eq for Impl {} + +impl PartialEq for Impl { + fn eq(&self, other: &Impl) -> bool { + let Impl { + impl_token: _, + ty, + brace_token: _, + } = self; + let Impl { + impl_token: _, + ty: ty2, + brace_token: _, + } = other; + ty == ty2 + } +} + +impl Borrow for &Impl { + fn borrow(&self) -> &Type { + &self.ty + } +} diff --git a/syntax/set.rs b/syntax/set.rs index 891df60..73e3909 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -35,6 +35,14 @@ where { self.set.contains(value) } + + pub fn get(&self, value: &Q) -> Option<&'a T> + where + &'a T: Borrow, + Q: ?Sized + Hash + Eq, + { + self.set.get(value).copied() + } } impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> { diff --git a/syntax/types.rs b/syntax/types.rs index 2afce25..3f8d10c 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type, TypeAlias}; +use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -15,7 +15,7 @@ pub struct Types<'a> { pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, - pub explicit_impls: Set<&'a Type>, + pub explicit_impls: Set<&'a Impl>, } impl<'a> Types<'a> { @@ -137,7 +137,7 @@ impl<'a> Types<'a> { } Api::Impl(imp) => { visit(&mut all, &imp.ty); - explicit_impls.insert(&imp.ty); + explicit_impls.insert(imp); } } } diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr index d77d3ff..5686cf1 100644 --- a/tests/ui/unique_ptr_twice.stderr +++ b/tests/ui/unique_ptr_twice.stderr @@ -1,10 +1,8 @@ error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`: - --> $DIR/unique_ptr_twice.rs:10:1 + --> $DIR/unique_ptr_twice.rs:16:5 | -1 | #[cxx::bridge] - | -------------- first implementation here +7 | impl UniquePtr {} + | ----------------- first implementation here ... -10 | #[cxx::bridge] - | ^^^^^^^^^^^^^^ conflicting implementation for `here::C` - | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +16 | impl UniquePtr {} + | ^^^^^^^^^^^^^^^^^ conflicting implementation for `here::C` From d93afe7a92bc8ba403c72d825c7e759e4f9cb692 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 20:07:23 +0000 Subject: [PATCH 980/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index d4c77b9..f07de50 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -40,7 +40,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.21/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]), visibility = ["PUBLIC"], features = [ "proc-macro", @@ -64,7 +64,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.41/src/**"]), + srcs = glob(["vendor/syn-1.0.42/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index e5cb808..0d602a8 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -45,7 +45,7 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.21/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]), crate_features = [ "proc-macro", "span-locations", @@ -69,7 +69,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.41/src/**"]), + srcs = glob(["vendor/syn-1.0.42/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 1c43f70..00ecb95 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -171,24 +171,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" [[package]] name = "link-cplusplus" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f563b3814ea63e830e3e321206e4e2b5177854586ebe3f595795ed7053b217f5" +checksum = "372d61b8ffdc79aa85d5f679e16c9e34da2357796186e877001f21998ece1f99" dependencies = [ "cc", ] [[package]] name = "proc-macro2" -version = "1.0.21" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -241,9 +241,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" dependencies = [ "itoa", "ryu", @@ -258,9 +258,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" dependencies = [ "proc-macro2", "quote", From fd0034ecf32a1f53d666782dc14c8de56488c5b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 20:15:34 +0000 Subject: [PATCH 981/2232] Add some thoughts about the triviality static assertions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index b83611a..1a071a2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -418,6 +418,24 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { + // NOTE: The following two static assertions are just nice-to-have and not + // necessary for soundness. That's because triviality is always declared by + // the user in the form of an unsafe impl of cxx::ExternType: + // + // unsafe impl ExternType for MyType { + // type Id = cxx::type_id!("..."); + // type Kind = cxx::kind::Trivial; + // } + // + // Since the user went on the record with their unsafe impl to unsafely + // claim they KNOW that the type is trivial, it's fine for that to be on + // them if that were wrong. + // + // There may be a legitimate reason we'll want to remove these assertions + // for support of types that the programmer knows are Rust-movable despite + // not being recognized as such by the C++ type system due to a move + // constructor or destructor. + writeln!(out, "static_assert("); writeln!( out, From e9a7d1ae0b8245edd45d6a5546b3b6b6241aa07b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 20:22:33 +0000 Subject: [PATCH 982/2232] Merge pull request #338 from dtolnay/trivial Add some thoughts about the triviality static assertions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index b83611a..1a071a2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -418,6 +418,24 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { } fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { + // NOTE: The following two static assertions are just nice-to-have and not + // necessary for soundness. That's because triviality is always declared by + // the user in the form of an unsafe impl of cxx::ExternType: + // + // unsafe impl ExternType for MyType { + // type Id = cxx::type_id!("..."); + // type Kind = cxx::kind::Trivial; + // } + // + // Since the user went on the record with their unsafe impl to unsafely + // claim they KNOW that the type is trivial, it's fine for that to be on + // them if that were wrong. + // + // There may be a legitimate reason we'll want to remove these assertions + // for support of types that the programmer knows are Rust-movable despite + // not being recognized as such by the C++ type system due to a move + // constructor or destructor. + writeln!(out, "static_assert("); writeln!( out, From cc44c7abeadb45fc9afffd04ff274d203b65f226 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 04 2020 20:22:39 +0000 Subject: [PATCH 983/2232] Consistently use ::std in generated code --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 1a071a2..328c166 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -322,10 +322,10 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "missing trycatch(...);"); writeln!(out); writeln!(out, "template "); - writeln!(out, "static typename std::enable_if<"); + writeln!(out, "static typename ::std::enable_if<"); writeln!( out, - " std::is_same(), std::declval())),", + " ::std::is_same(), ::std::declval())),", ); writeln!(out, " missing>::value>::type"); writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); @@ -439,7 +439,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { writeln!(out, "static_assert("); writeln!( out, - " std::is_trivially_move_constructible<{}>::value,", + " ::std::is_trivially_move_constructible<{}>::value,", id, ); writeln!( @@ -448,7 +448,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { id, ); writeln!(out, "static_assert("); - writeln!(out, " std::is_trivially_destructible<{}>::value,", id); + writeln!(out, " ::std::is_trivially_destructible<{}>::value,", id); writeln!( out, " \"type {} marked as Trivial in Rust is not trivially destructible in C++\");", From 821402b2a6849cf4382d8a0ca3a1af010dfdb0bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 02:50:35 +0000 Subject: [PATCH 984/2232] Move C++ generated code test to cxx's integration test --- diff --git a/Cargo.toml b/Cargo.toml index 57b4320..d8b93f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ cxxbridge-flags = { version = "=0.4.7", path = "flags", default-features = false [dev-dependencies] cxx-build = { version = "=0.4.7", path = "gen/build" } +cxx-gen = { version = "0.4", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 8522715..2fee0e9 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -8,9 +8,6 @@ pub(super) mod include; pub(super) mod out; mod write; -#[cfg(test)] -mod tests; - pub(super) use self::error::Error; use self::error::{format_err, Result}; use self::file::File; diff --git a/gen/src/tests.rs b/gen/src/tests.rs deleted file mode 100644 index dbf4c62..0000000 --- a/gen/src/tests.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::gen::{generate_from_string, Opt}; - -const CPP_EXAMPLE: &str = r#" - #[cxx::bridge] - mod ffi { - extern "C" { - pub fn do_cpp_thing(foo: &str); - } - } -"#; - -#[test] -fn test_cpp() { - let opts = Opt { - include: Vec::new(), - cxx_impl_annotations: None, - gen_header: false, - gen_implementation: true, - }; - let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap(); - let output = std::str::from_utf8(&output.implementation).unwrap(); - // To avoid continual breakage we won't test every byte. - // Let's look for the major features. - assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); -} - -#[test] -fn test_annotation() { - let opts = Opt { - include: Vec::new(), - cxx_impl_annotations: Some("ANNOTATION".to_string()), - gen_header: false, - gen_implementation: true, - }; - let output = generate_from_string(CPP_EXAMPLE, &opts).unwrap(); - let output = std::str::from_utf8(&output.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); -} diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs new file mode 100644 index 0000000..b5ff106 --- /dev/null +++ b/tests/cxx_gen.rs @@ -0,0 +1,31 @@ +use cxx_gen::{generate_header_and_cc, Opt}; + +const CPP_EXAMPLE: &str = r#" + #[cxx::bridge] + mod ffi { + extern "C" { + pub fn do_cpp_thing(foo: &str); + } + } +"#; + +#[test] +fn test_cpp() { + let opt = Opt::default(); + let source = CPP_EXAMPLE.parse().unwrap(); + let output = generate_header_and_cc(source, &opt).unwrap(); + let output = std::str::from_utf8(&output.implementation).unwrap(); + // To avoid continual breakage we won't test every byte. + // Let's look for the major features. + assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); +} + +#[test] +fn test_annotation() { + let mut opt = Opt::default(); + opt.cxx_impl_annotations = Some("ANNOTATION".to_owned()); + let source = CPP_EXAMPLE.parse().unwrap(); + let output = generate_header_and_cc(source, &opt).unwrap(); + let output = std::str::from_utf8(&output.implementation).unwrap(); + assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); +} From 1f7a7e5bca38e204bb31243fca633220784de96d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 02:50:35 +0000 Subject: [PATCH 985/2232] Touch up cxx_gen test --- diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs index b5ff106..139950e 100644 --- a/tests/cxx_gen.rs +++ b/tests/cxx_gen.rs @@ -1,6 +1,7 @@ use cxx_gen::{generate_header_and_cc, Opt}; +use std::str; -const CPP_EXAMPLE: &str = r#" +const BRIDGE0: &str = r#" #[cxx::bridge] mod ffi { extern "C" { @@ -10,22 +11,22 @@ const CPP_EXAMPLE: &str = r#" "#; #[test] -fn test_cpp() { +fn test_extern_c_function() { let opt = Opt::default(); - let source = CPP_EXAMPLE.parse().unwrap(); - let output = generate_header_and_cc(source, &opt).unwrap(); - let output = std::str::from_utf8(&output.implementation).unwrap(); + let source = BRIDGE0.parse().unwrap(); + let generated = generate_header_and_cc(source, &opt).unwrap(); + let output = str::from_utf8(&generated.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); } #[test] -fn test_annotation() { +fn test_impl_annotation() { let mut opt = Opt::default(); opt.cxx_impl_annotations = Some("ANNOTATION".to_owned()); - let source = CPP_EXAMPLE.parse().unwrap(); - let output = generate_header_and_cc(source, &opt).unwrap(); - let output = std::str::from_utf8(&output.implementation).unwrap(); + let source = BRIDGE0.parse().unwrap(); + let generated = generate_header_and_cc(source, &opt).unwrap(); + let output = str::from_utf8(&generated.implementation).unwrap(); assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); } From b22ac4e4b9fc55479de2bed3712a9cc1c8e5dba6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 03:01:00 +0000 Subject: [PATCH 986/2232] Reflect new dev dependency on cxx-gen in lockfile --- diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 00ecb95..041c9cc 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -63,6 +63,7 @@ version = "0.4.7" dependencies = [ "cc", "cxx-build", + "cxx-gen", "cxx-test-suite", "cxxbridge-flags", "cxxbridge-macro", From f57f75668915b15cfcc17fa324ed13adeb39a4b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 03:01:00 +0000 Subject: [PATCH 987/2232] Move include to the place its functions are used --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 328c166..1048272 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -138,14 +138,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => {} - None => { - if types.aliases.contains_key(ident) - && types.required_trivial.contains_key(ident) - { - out.include.type_traits = true; - } - } + Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} }, Type::RustBox(_) => out.include.type_traits = true, Type::UniquePtr(_) => out.include.memory = true, @@ -436,6 +429,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { // not being recognized as such by the C++ type system due to a move // constructor or destructor. + out.include.type_traits = true; writeln!(out, "static_assert("); writeln!( out, From 2c4b35f486ae4c14d60a9fd2239ee1be57460403 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 04:22:34 +0000 Subject: [PATCH 988/2232] Format in a way that rustfmt 1.x and 2.0.0-rc.2 both render the same --- diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 2a731d7..7408d40 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -190,7 +190,9 @@ unsafe impl UniquePtrTarget for CxxString { const __NAME: &'static dyn Display = &"CxxString"; fn __null() -> *mut c_void { let mut repr = ptr::null_mut::(); - unsafe { unique_ptr_std_string_null(&mut repr) } + unsafe { + unique_ptr_std_string_null(&mut repr); + } repr } unsafe fn __raw(raw: *mut Self) -> *mut c_void { diff --git a/syntax/parse.rs b/syntax/parse.rs index 49dc26b..c611c8b 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -73,7 +73,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { Fields::Named(fields) => fields, Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")), Fields::Unnamed(_) => { - return Err(Error::new_spanned(item, "tuple structs are not supported")) + return Err(Error::new_spanned(item, "tuple structs are not supported")); } }; From b8543bd30c4b3bb2e528643082f85a296ed85c08 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 20:28:27 +0000 Subject: [PATCH 989/2232] Tweak wording of the triviality requirement It isn't actually a requirement that the type have no destructor as brought up by the previous paragraph. Rather, it just needs to be safe for values of that type to be moved via Rust's memcpy-based move semantics. The distinction is relevant e.g. for `struct S { s: String }` which would have a C++ destructor and nontrivial move constructor but still be safely memcpy movable. --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 0432096..b9c5386 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -123,7 +123,7 @@ pub unsafe trait ExternType { /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible /// /// If you believe your C++ type reflected by this ExternType impl is indeed - /// trivial, you can specify: + /// fine to hold by value and move in Rust, you can specify: /// /// ``` /// # struct TypeName; From 942053246687c1e1578ed0bc0736a81c53a71300 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 05 2020 22:47:19 +0000 Subject: [PATCH 990/2232] Merge pull request #339 from dtolnay/triviality Tweak wording of the triviality requirement --- diff --git a/src/extern_type.rs b/src/extern_type.rs index 0432096..b9c5386 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -123,7 +123,7 @@ pub unsafe trait ExternType { /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible /// /// If you believe your C++ type reflected by this ExternType impl is indeed - /// trivial, you can specify: + /// fine to hold by value and move in Rust, you can specify: /// /// ``` /// # struct TypeName; From 8684cc51ad3a9a4d988e462138c902b89c7a029d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 00:09:48 +0000 Subject: [PATCH 991/2232] Emit an ExternType impl for shared structs and enums --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 13134d0..dd5350f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -41,8 +41,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(strct)), - Api::Enum(enm) => expanded.extend(expand_enum(enm)), + Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), + Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), Api::CxxType(ety) => { let ident = &ety.ident; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { @@ -125,16 +125,18 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(strct: &Struct) -> TokenStream { +fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; let derives = &strct.derives; + let type_id = type_id(namespace, ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. let vis = Token![pub](field.ident.span()); quote!(#vis #field) }); + quote! { #doc #[derive(#(#derives),*)] @@ -142,13 +144,19 @@ fn expand_struct(strct: &Struct) -> TokenStream { pub struct #ident { #(#fields,)* } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + type Kind = ::cxx::kind::Trivial; + } } } -fn expand_enum(enm: &Enum) -> TokenStream { +fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; + let type_id = type_id(namespace, ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -156,6 +164,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); + quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] @@ -168,6 +177,11 @@ fn expand_enum(enm: &Enum) -> TokenStream { impl #ident { #(#variants)* } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + type Kind = ::cxx::kind::Trivial; + } } } From 4396abfafc69d5fe65fb10a5393a001d700a5043 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 00:20:11 +0000 Subject: [PATCH 992/2232] Merge pull request #341 from dtolnay/shared Emit an ExternType impl for shared structs and enums --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 13134d0..dd5350f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -41,8 +41,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(strct)), - Api::Enum(enm) => expanded.extend(expand_enum(enm)), + Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), + Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), Api::CxxType(ety) => { let ident = &ety.ident; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { @@ -125,16 +125,18 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(strct: &Struct) -> TokenStream { +fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; let derives = &strct.derives; + let type_id = type_id(namespace, ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. let vis = Token![pub](field.ident.span()); quote!(#vis #field) }); + quote! { #doc #[derive(#(#derives),*)] @@ -142,13 +144,19 @@ fn expand_struct(strct: &Struct) -> TokenStream { pub struct #ident { #(#fields,)* } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + type Kind = ::cxx::kind::Trivial; + } } } -fn expand_enum(enm: &Enum) -> TokenStream { +fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; + let type_id = type_id(namespace, ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -156,6 +164,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); + quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] @@ -168,6 +177,11 @@ fn expand_enum(enm: &Enum) -> TokenStream { impl #ident { #(#variants)* } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + type Kind = ::cxx::kind::Trivial; + } } } From 870bc01e459a9be43159b9923bb39294b722d248 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 00:52:34 +0000 Subject: [PATCH 993/2232] Recognize CARGO_TARGET_DIR env var if set --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 35b6606..7db8a4c 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -1,4 +1,5 @@ use crate::paths::TargetDir; +use std::env; use std::path::{Path, PathBuf}; use std::process::Command; use std::str; @@ -8,6 +9,10 @@ pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { } fn try_target_dir(out_dir: &Path) -> Option { + if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { + return Some(PathBuf::from(target_dir)); + } + let cargo = option_env!("CARGO").unwrap_or("cargo"); let output = Command::new(cargo) .current_dir(out_dir) From 69080574d52fc85c5ec81f84e808ba62a27801a6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 00:54:45 +0000 Subject: [PATCH 994/2232] Skip trying to call `cargo metadata` if CARGO_TARGET_DIR is relative --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs index 7db8a4c..18b6b6f 100644 --- a/gen/build/src/cargo.rs +++ b/gen/build/src/cargo.rs @@ -10,7 +10,12 @@ pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { fn try_target_dir(out_dir: &Path) -> Option { if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { - return Some(PathBuf::from(target_dir)); + let target_dir = PathBuf::from(target_dir); + if target_dir.is_absolute() { + return Some(target_dir); + } else { + return None; + }; } let cargo = option_env!("CARGO").unwrap_or("cargo"); From 59b103fe5cd409cb37457571ae701bab07744e3d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 01:22:15 +0000 Subject: [PATCH 995/2232] Remove extraneous create_dir_all The directory creation is already handled by out::write. --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index ef284c4..838908b 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -62,7 +62,7 @@ mod syntax; use crate::error::Result; use crate::gen::error::report; -use crate::gen::{fs, Opt}; +use crate::gen::Opt; use crate::paths::{PathExt, TargetDir}; use cc::Build; use std::io::{self, Write}; @@ -148,9 +148,7 @@ fn write_header(prj: &Project) { let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); if let TargetDir::Path(target_dir) = &prj.target_dir { - let ref header_dir = target_dir.join("cxxbridge").join("rust"); - let _ = fs::create_dir_all(header_dir); - let ref cxx_h = header_dir.join("cxx.h"); + let ref cxx_h = target_dir.join("cxxbridge").join("rust").join("cxx.h"); let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); } } From ee907be404300e5101e9a1b6bf598bd44a5cd400 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:14:53 +0000 Subject: [PATCH 996/2232] Use Cargo's build script metadata feature to make reliable include dirs --- diff --git a/BUCK b/BUCK index 695c079..0505bd2 100644 --- a/BUCK +++ b/BUCK @@ -48,6 +48,7 @@ rust_library( rust_library( name = "build", srcs = glob(["gen/build/src/**"]), + env = {"OUT_DIR": ""}, visibility = ["PUBLIC"], deps = [ "//third-party:cc", diff --git a/BUILD b/BUILD index 4700e06..e551d18 100644 --- a/BUILD +++ b/BUILD @@ -54,6 +54,7 @@ rust_library( name = "build", srcs = glob(["gen/build/src/**/*.rs"]), data = ["gen/build/src/gen/include/cxx.h"], + rustc_env = {"OUT_DIR": ""}, visibility = ["//visibility:public"], deps = [ "//third-party:cc", diff --git a/build.rs b/build.rs index 502a60b..688b5b7 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,6 @@ +use std::env; +use std::path::Path; + fn main() { cc::Build::new() .file("src/cxx.cc") @@ -8,4 +11,8 @@ fn main() { println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); println!("cargo:rustc-cfg=built_with_cargo"); + if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") { + let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h"); + println!("cargo:HEADER={}", cxx_h.to_string_lossy()); + } } diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index 0ed4d74..2fbd41d 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,19 +1,22 @@ use crate::gen::fs; use std::error::Error as StdError; +use std::ffi::OsString; use std::fmt::{self, Display}; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { - MissingOutDir, + NoEnv(OsString), Fs(fs::Error), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), + Error::NoEnv(var) => { + write!(f, "missing {} environment variable", var.to_string_lossy()) + } Error::Fs(err) => err.fmt(f), } } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 838908b..0b4ae98 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -60,11 +60,13 @@ mod out; mod paths; mod syntax; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::Opt; use crate::paths::{PathExt, TargetDir}; use cc::Build; +use std::env; +use std::ffi::{OsStr, OsString}; use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; @@ -100,12 +102,29 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } struct Project { + package_name: OsString, + manifest_dir: PathBuf, + // Output directory as received from Cargo. out_dir: PathBuf, - target_dir: TargetDir, + // Directory into which to symlink all generated code. + // + // This is *not* used for an #include path, only as a debugging convenience. + // Normally available at target/cxxbridge/ if we are able to know where the + // target dir is, otherwise under a common scratch dir. + // + // The reason this isn't the #include dir is that we do not want builds to + // have access to headers from arbitrary other parts of the dependency + // graph. Using a global directory for all builds would be both a race + // condition depending on what order Cargo randomly executes the build + // scripts, as well as semantically undesirable for builds not to have to + // declare their real dependencies. + shared_dir: PathBuf, } impl Project { fn init() -> Result { + let package_name = env_os("CARGO_PKG_NAME")?; + let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; let target_dir = match cargo::target_dir(&out_dir) { @@ -114,60 +133,89 @@ impl Project { TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), }; + let shared_dir = match target_dir { + TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), + // Use cxx-build's OUT_DIR. + TargetDir::Unknown => PathBuf::from(env!("OUT_DIR")), + }; + Ok(Project { + package_name, + manifest_dir, out_dir, - target_dir, + shared_dir, }) } } +// We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge +// subdirectory to avoid stomping on other things that the caller's build script +// might be doing inside OUT_DIR. +// +// $OUT_DIR/ +// cxxbridge/ +// crate/ +// $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR +// include/ +// rust/ +// cxx.h +// $CARGO_PKG_NAME/ +// .../ +// lib.rs.h +// sources/ +// $CARGO_PKG_NAME/ +// .../ +// lib.rs.cc +// +// The crate/ and include/ directories are placed on the #include path. fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; - let include_dir = paths::include_dir(prj); + + let ref crate_dir = make_crate_dir(prj); + let ref include_dir = make_include_dir(prj)?; let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - build.include(&include_dir); - write_header(prj); - let crate_dir = symlink_crate(prj, &mut build); + if let Some(crate_dir) = crate_dir { + build.include(crate_dir); + } + build.include(include_dir); for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; } eprintln!("\nCXX include path:"); - eprintln!(" {}", include_dir.display()); if let Some(crate_dir) = crate_dir { eprintln!(" {}", crate_dir.display()); } + eprintln!(" {}", include_dir.display()); Ok(build) } -fn write_header(prj: &Project) { - let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); - let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); - if let TargetDir::Path(target_dir) = &prj.target_dir { - let ref cxx_h = target_dir.join("cxxbridge").join("rust").join("cxx.h"); - let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); +fn make_crate_dir(prj: &Project) -> Option { + let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); + let link = crate_dir.join(&prj.package_name); + if out::symlink_dir(&prj.manifest_dir, link).is_ok() { + Some(crate_dir) + } else { + None } } -fn symlink_crate(prj: &Project, build: &mut Build) -> Option { - let manifest_dir = match paths::manifest_dir() { - Some(manifest_dir) => manifest_dir, - None => return None, - }; - let package_name = match paths::package_name() { - Some(package_name) => package_name, - None => return None, - }; - - let mut link = paths::include_dir(prj); - link.push("CRATE"); - let _ = out::symlink_dir(manifest_dir, link.join(package_name)); - build.include(&link); - Some(link) +fn make_include_dir(prj: &Project) -> Result { + let include_dir = prj.out_dir.join("cxxbridge").join("include"); + let cxx_h = include_dir.join("rust").join("cxx.h"); + let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); + if let Some(ref original) = env::var_os("DEP_CXXBRIDGE04_HEADER") { + out::symlink_file(original, cxx_h)?; + out::symlink_file(original, shared_cxx_h)?; + } else { + out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; + out::symlink_file(shared_cxx_h, cxx_h)?; + } + Ok(include_dir) } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { @@ -175,21 +223,30 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let generated = gen::generate_from_path(rust_source_file, &opt); let ref rel_path = paths::local_relative_path(rust_source_file); + let cxxbridge = prj.out_dir.join("cxxbridge"); + let include_dir = cxxbridge.join("include").join(&prj.package_name); + let sources_dir = cxxbridge.join("sources").join(&prj.package_name); + let ref rel_path_h = rel_path.with_appended_extension(".h"); - let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); + let ref header_path = include_dir.join(rel_path_h); out::write(header_path, &generated.header)?; - let ref link_path = paths::namespaced(&prj.out_dir, rel_path); + let ref link_path = include_dir.join(rel_path); let _ = out::symlink_file(header_path, link_path); - if let TargetDir::Path(target_dir) = &prj.target_dir { - let ref link_path = paths::namespaced(target_dir, rel_path); - let _ = out::symlink_file(header_path, link_path); - let _ = out::symlink_file(header_path, link_path.with_appended_extension(".h")); - } let ref rel_path_cc = rel_path.with_appended_extension(".cc"); - let ref implementation_path = paths::namespaced(&prj.out_dir, rel_path_cc); + let ref implementation_path = sources_dir.join(rel_path_cc); out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); + + let shared_h = prj.shared_dir.join(&prj.package_name).join(rel_path_h); + let shared_cc = prj.shared_dir.join(&prj.package_name).join(rel_path_cc); + let _ = out::symlink_file(header_path, shared_h); + let _ = out::symlink_file(implementation_path, shared_cc); Ok(()) } + +fn env_os(key: impl AsRef) -> Result { + let key = key.as_ref(); + env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned())) +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 1007730..56c0018 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,8 +1,6 @@ -use crate::error::{Error, Result}; +use crate::error::Result; use crate::gen::fs; -use crate::Project; -use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; pub(crate) enum TargetDir { @@ -10,10 +8,12 @@ pub(crate) enum TargetDir { Unknown, } +pub(crate) fn manifest_dir() -> Result { + crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from) +} + pub(crate) fn out_dir() -> Result { - env::var_os("OUT_DIR") - .map(PathBuf::from) - .ok_or(Error::MissingOutDir) + crate::env_os("OUT_DIR").map(PathBuf::from) } // Given a path provided by the user, determines where generated files related @@ -32,14 +32,6 @@ pub(crate) fn local_relative_path(path: &Path) -> PathBuf { rel_path } -pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf { - let mut path = base.to_owned(); - path.push("cxxbridge"); - path.extend(package_name()); - path.push(rel_path); - path -} - pub(crate) trait PathExt { fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; } @@ -52,21 +44,6 @@ impl PathExt for Path { } } -pub(crate) fn include_dir(prj: &Project) -> PathBuf { - match &prj.target_dir { - TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), - TargetDir::Unknown => prj.out_dir.join("cxxbridge"), - } -} - -pub(crate) fn manifest_dir() -> Option { - env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) -} - -pub(crate) fn package_name() -> Option { - env::var_os("CARGO_PKG_NAME") -} - pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to // handle in includes. From 45ded8884695c4ece59bbf27df0e12ef6cd4dd05 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:14:53 +0000 Subject: [PATCH 997/2232] Swap include line order to support #include of a .rs file --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 0b4ae98..538ef12 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -177,10 +177,14 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate + build.include(include_dir); if let Some(crate_dir) = crate_dir { + // Placed after the generated code directory (include_dir) on the + // include line so that `#include "path/to/file.rs"` from C++ + // "magically" works and refers to the API generated from that Rust + // source file. build.include(crate_dir); } - build.include(include_dir); for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; From d7c7e675551f051933d36cf7e30dbd808b27195f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:14:53 +0000 Subject: [PATCH 998/2232] Place direct dependencies on the include path --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 538ef12..3782543 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -65,6 +65,7 @@ use crate::gen::error::report; use crate::gen::Opt; use crate::paths::{PathExt, TargetDir}; use cc::Build; +use std::collections::BTreeMap; use std::env; use std::ffi::{OsStr, OsString}; use std::io::{self, Write}; @@ -167,7 +168,9 @@ impl Project { // .../ // lib.rs.cc // -// The crate/ and include/ directories are placed on the #include path. +// The crate/ and include/ directories are placed on the #include path for the +// current build as well as for downstream builds that have a direct dependency +// on the current crate. fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; @@ -177,31 +180,69 @@ fn build(rust_source_files: &mut dyn Iterator>) -> Resul let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate + + for path in rust_source_files { + generate_bridge(prj, &mut build, path.as_ref())?; + } + + eprintln!("\nCXX include path:"); build.include(include_dir); + eprintln!(" {}", include_dir.display()); if let Some(crate_dir) = crate_dir { // Placed after the generated code directory (include_dir) on the // include line so that `#include "path/to/file.rs"` from C++ // "magically" works and refers to the API generated from that Rust // source file. build.include(crate_dir); + eprintln!(" {}", crate_dir.display()); } - - for path in rust_source_files { - generate_bridge(prj, &mut build, path.as_ref())?; + for dep in env_include_dirs() { + build.include(&dep); + eprintln!(" {}", dep.display()); } + Ok(build) +} - eprintln!("\nCXX include path:"); - if let Some(crate_dir) = crate_dir { - eprintln!(" {}", crate_dir.display()); +fn env_include_dirs() -> impl Iterator { + let mut env_include_dirs = BTreeMap::new(); + for (k, v) in env::vars_os() { + let mut k = k.to_string_lossy().into_owned(); + // Only variables set from a build script of direct dependencies are + // observable. That's exactly what we want! Your crate needs to declare + // a direct dependency on the other crate in order to be able to + // #include its headers. + // + // Also, they're only observable if the dependency's manifest contains a + // `links` key. This is important because Cargo imposes no ordering on + // the execution of build scripts without a `links` key. When exposing a + // generated header for the current crate to #include, we need to be + // sure the dependency's build script has already executed and emitted + // that generated header. + // + // References: + // - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key + // - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate + if k.starts_with("DEP_") { + if k.ends_with("_CXXBRIDGE_INCLUDE") { + // Tweak to ensure sorted before the other one, for the same + // reason as the comment on ordering of include_dir vs crate_dir + // above. + k.replace_range(k.len() - "INCLUDE".len().., "0"); + env_include_dirs.insert(k, PathBuf::from(v)); + } else if k.ends_with("_CXXBRIDGE_CRATE") { + k.replace_range(k.len() - "CRATE".len().., "1"); + env_include_dirs.insert(k, PathBuf::from(v)); + } + } } - eprintln!(" {}", include_dir.display()); - Ok(build) + env_include_dirs.into_iter().map(|entry| entry.1) } fn make_crate_dir(prj: &Project) -> Option { let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); let link = crate_dir.join(&prj.package_name); if out::symlink_dir(&prj.manifest_dir, link).is_ok() { + println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); Some(crate_dir) } else { None @@ -219,6 +260,7 @@ fn make_include_dir(prj: &Project) -> Result { out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; out::symlink_file(shared_cxx_h, cxx_h)?; } + println!("cargo:CXXBRIDGE_INCLUDE={}", include_dir.to_string_lossy()); Ok(include_dir) } From 1f7edf8d2723bfa21cd061add727fde94b161fe4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:38:43 +0000 Subject: [PATCH 999/2232] Merge pull request #346 from dtolnay/build Use Cargo's build script metadata feature to make reliable include dirs --- diff --git a/BUCK b/BUCK index 695c079..0505bd2 100644 --- a/BUCK +++ b/BUCK @@ -48,6 +48,7 @@ rust_library( rust_library( name = "build", srcs = glob(["gen/build/src/**"]), + env = {"OUT_DIR": ""}, visibility = ["PUBLIC"], deps = [ "//third-party:cc", diff --git a/BUILD b/BUILD index 4700e06..e551d18 100644 --- a/BUILD +++ b/BUILD @@ -54,6 +54,7 @@ rust_library( name = "build", srcs = glob(["gen/build/src/**/*.rs"]), data = ["gen/build/src/gen/include/cxx.h"], + rustc_env = {"OUT_DIR": ""}, visibility = ["//visibility:public"], deps = [ "//third-party:cc", diff --git a/build.rs b/build.rs index 502a60b..688b5b7 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,6 @@ +use std::env; +use std::path::Path; + fn main() { cc::Build::new() .file("src/cxx.cc") @@ -8,4 +11,8 @@ fn main() { println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); println!("cargo:rustc-cfg=built_with_cargo"); + if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") { + let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h"); + println!("cargo:HEADER={}", cxx_h.to_string_lossy()); + } } diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs index 0ed4d74..2fbd41d 100644 --- a/gen/build/src/error.rs +++ b/gen/build/src/error.rs @@ -1,19 +1,22 @@ use crate::gen::fs; use std::error::Error as StdError; +use std::ffi::OsString; use std::fmt::{self, Display}; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { - MissingOutDir, + NoEnv(OsString), Fs(fs::Error), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"), + Error::NoEnv(var) => { + write!(f, "missing {} environment variable", var.to_string_lossy()) + } Error::Fs(err) => err.fmt(f), } } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 838908b..3782543 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -60,11 +60,14 @@ mod out; mod paths; mod syntax; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::Opt; use crate::paths::{PathExt, TargetDir}; use cc::Build; +use std::collections::BTreeMap; +use std::env; +use std::ffi::{OsStr, OsString}; use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; @@ -100,12 +103,29 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } struct Project { + package_name: OsString, + manifest_dir: PathBuf, + // Output directory as received from Cargo. out_dir: PathBuf, - target_dir: TargetDir, + // Directory into which to symlink all generated code. + // + // This is *not* used for an #include path, only as a debugging convenience. + // Normally available at target/cxxbridge/ if we are able to know where the + // target dir is, otherwise under a common scratch dir. + // + // The reason this isn't the #include dir is that we do not want builds to + // have access to headers from arbitrary other parts of the dependency + // graph. Using a global directory for all builds would be both a race + // condition depending on what order Cargo randomly executes the build + // scripts, as well as semantically undesirable for builds not to have to + // declare their real dependencies. + shared_dir: PathBuf, } impl Project { fn init() -> Result { + let package_name = env_os("CARGO_PKG_NAME")?; + let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; let target_dir = match cargo::target_dir(&out_dir) { @@ -114,60 +134,134 @@ impl Project { TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), }; + let shared_dir = match target_dir { + TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), + // Use cxx-build's OUT_DIR. + TargetDir::Unknown => PathBuf::from(env!("OUT_DIR")), + }; + Ok(Project { + package_name, + manifest_dir, out_dir, - target_dir, + shared_dir, }) } } +// We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge +// subdirectory to avoid stomping on other things that the caller's build script +// might be doing inside OUT_DIR. +// +// $OUT_DIR/ +// cxxbridge/ +// crate/ +// $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR +// include/ +// rust/ +// cxx.h +// $CARGO_PKG_NAME/ +// .../ +// lib.rs.h +// sources/ +// $CARGO_PKG_NAME/ +// .../ +// lib.rs.cc +// +// The crate/ and include/ directories are placed on the #include path for the +// current build as well as for downstream builds that have a direct dependency +// on the current crate. fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; - let include_dir = paths::include_dir(prj); + + let ref crate_dir = make_crate_dir(prj); + let ref include_dir = make_include_dir(prj)?; let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate - build.include(&include_dir); - write_header(prj); - let crate_dir = symlink_crate(prj, &mut build); for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; } eprintln!("\nCXX include path:"); + build.include(include_dir); eprintln!(" {}", include_dir.display()); if let Some(crate_dir) = crate_dir { + // Placed after the generated code directory (include_dir) on the + // include line so that `#include "path/to/file.rs"` from C++ + // "magically" works and refers to the API generated from that Rust + // source file. + build.include(crate_dir); eprintln!(" {}", crate_dir.display()); } + for dep in env_include_dirs() { + build.include(&dep); + eprintln!(" {}", dep.display()); + } Ok(build) } -fn write_header(prj: &Project) { - let ref cxx_h = prj.out_dir.join("cxxbridge").join("rust").join("cxx.h"); - let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); - if let TargetDir::Path(target_dir) = &prj.target_dir { - let ref cxx_h = target_dir.join("cxxbridge").join("rust").join("cxx.h"); - let _ = out::write(cxx_h, gen::include::HEADER.as_bytes()); +fn env_include_dirs() -> impl Iterator { + let mut env_include_dirs = BTreeMap::new(); + for (k, v) in env::vars_os() { + let mut k = k.to_string_lossy().into_owned(); + // Only variables set from a build script of direct dependencies are + // observable. That's exactly what we want! Your crate needs to declare + // a direct dependency on the other crate in order to be able to + // #include its headers. + // + // Also, they're only observable if the dependency's manifest contains a + // `links` key. This is important because Cargo imposes no ordering on + // the execution of build scripts without a `links` key. When exposing a + // generated header for the current crate to #include, we need to be + // sure the dependency's build script has already executed and emitted + // that generated header. + // + // References: + // - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key + // - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate + if k.starts_with("DEP_") { + if k.ends_with("_CXXBRIDGE_INCLUDE") { + // Tweak to ensure sorted before the other one, for the same + // reason as the comment on ordering of include_dir vs crate_dir + // above. + k.replace_range(k.len() - "INCLUDE".len().., "0"); + env_include_dirs.insert(k, PathBuf::from(v)); + } else if k.ends_with("_CXXBRIDGE_CRATE") { + k.replace_range(k.len() - "CRATE".len().., "1"); + env_include_dirs.insert(k, PathBuf::from(v)); + } + } } + env_include_dirs.into_iter().map(|entry| entry.1) } -fn symlink_crate(prj: &Project, build: &mut Build) -> Option { - let manifest_dir = match paths::manifest_dir() { - Some(manifest_dir) => manifest_dir, - None => return None, - }; - let package_name = match paths::package_name() { - Some(package_name) => package_name, - None => return None, - }; - - let mut link = paths::include_dir(prj); - link.push("CRATE"); - let _ = out::symlink_dir(manifest_dir, link.join(package_name)); - build.include(&link); - Some(link) +fn make_crate_dir(prj: &Project) -> Option { + let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); + let link = crate_dir.join(&prj.package_name); + if out::symlink_dir(&prj.manifest_dir, link).is_ok() { + println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); + Some(crate_dir) + } else { + None + } +} + +fn make_include_dir(prj: &Project) -> Result { + let include_dir = prj.out_dir.join("cxxbridge").join("include"); + let cxx_h = include_dir.join("rust").join("cxx.h"); + let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); + if let Some(ref original) = env::var_os("DEP_CXXBRIDGE04_HEADER") { + out::symlink_file(original, cxx_h)?; + out::symlink_file(original, shared_cxx_h)?; + } else { + out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; + out::symlink_file(shared_cxx_h, cxx_h)?; + } + println!("cargo:CXXBRIDGE_INCLUDE={}", include_dir.to_string_lossy()); + Ok(include_dir) } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { @@ -175,21 +269,30 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let generated = gen::generate_from_path(rust_source_file, &opt); let ref rel_path = paths::local_relative_path(rust_source_file); + let cxxbridge = prj.out_dir.join("cxxbridge"); + let include_dir = cxxbridge.join("include").join(&prj.package_name); + let sources_dir = cxxbridge.join("sources").join(&prj.package_name); + let ref rel_path_h = rel_path.with_appended_extension(".h"); - let ref header_path = paths::namespaced(&prj.out_dir, rel_path_h); + let ref header_path = include_dir.join(rel_path_h); out::write(header_path, &generated.header)?; - let ref link_path = paths::namespaced(&prj.out_dir, rel_path); + let ref link_path = include_dir.join(rel_path); let _ = out::symlink_file(header_path, link_path); - if let TargetDir::Path(target_dir) = &prj.target_dir { - let ref link_path = paths::namespaced(target_dir, rel_path); - let _ = out::symlink_file(header_path, link_path); - let _ = out::symlink_file(header_path, link_path.with_appended_extension(".h")); - } let ref rel_path_cc = rel_path.with_appended_extension(".cc"); - let ref implementation_path = paths::namespaced(&prj.out_dir, rel_path_cc); + let ref implementation_path = sources_dir.join(rel_path_cc); out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); + + let shared_h = prj.shared_dir.join(&prj.package_name).join(rel_path_h); + let shared_cc = prj.shared_dir.join(&prj.package_name).join(rel_path_cc); + let _ = out::symlink_file(header_path, shared_h); + let _ = out::symlink_file(implementation_path, shared_cc); Ok(()) } + +fn env_os(key: impl AsRef) -> Result { + let key = key.as_ref(); + env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned())) +} diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 1007730..56c0018 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -1,8 +1,6 @@ -use crate::error::{Error, Result}; +use crate::error::Result; use crate::gen::fs; -use crate::Project; -use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; pub(crate) enum TargetDir { @@ -10,10 +8,12 @@ pub(crate) enum TargetDir { Unknown, } +pub(crate) fn manifest_dir() -> Result { + crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from) +} + pub(crate) fn out_dir() -> Result { - env::var_os("OUT_DIR") - .map(PathBuf::from) - .ok_or(Error::MissingOutDir) + crate::env_os("OUT_DIR").map(PathBuf::from) } // Given a path provided by the user, determines where generated files related @@ -32,14 +32,6 @@ pub(crate) fn local_relative_path(path: &Path) -> PathBuf { rel_path } -pub(crate) fn namespaced(base: &Path, rel_path: &Path) -> PathBuf { - let mut path = base.to_owned(); - path.push("cxxbridge"); - path.extend(package_name()); - path.push(rel_path); - path -} - pub(crate) trait PathExt { fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; } @@ -52,21 +44,6 @@ impl PathExt for Path { } } -pub(crate) fn include_dir(prj: &Project) -> PathBuf { - match &prj.target_dir { - TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), - TargetDir::Unknown => prj.out_dir.join("cxxbridge"), - } -} - -pub(crate) fn manifest_dir() -> Option { - env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) -} - -pub(crate) fn package_name() -> Option { - env::var_os("CARGO_PKG_NAME") -} - pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to // handle in includes. From de0c14d532ed1c6657b7d583457b93327cbc2682 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:41:44 +0000 Subject: [PATCH 1000/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index f07de50..e6a5611 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -7,7 +7,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.60/src/**"]), + srcs = glob(["vendor/cc-1.0.61/src/**"]), visibility = ["PUBLIC"], ) diff --git a/third-party/BUILD b/third-party/BUILD index 0d602a8..f874d07 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -12,7 +12,7 @@ rust_library( rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.60/src/**"]), + srcs = glob(["vendor/cc-1.0.61/src/**"]), visibility = ["//visibility:public"], ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 041c9cc..7e6a175 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -28,9 +28,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cc" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" [[package]] name = "clap" @@ -151,9 +151,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "hermit-abi" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] @@ -172,15 +172,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" [[package]] name = "link-cplusplus" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d61b8ffdc79aa85d5f679e16c9e34da2357796186e877001f21998ece1f99" +checksum = "f96aa785c87218ec773df6c510af203872b34e2df2cf47d6e908e5f36231e354" dependencies = [ "cc", ] From 4563fb1c746d4526d210e95b66d548632bbe72e8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:43:19 +0000 Subject: [PATCH 1001/2232] Add lazy_static dep to cxx-build --- diff --git a/BUCK b/BUCK index 0505bd2..22db155 100644 --- a/BUCK +++ b/BUCK @@ -53,6 +53,7 @@ rust_library( deps = [ "//third-party:cc", "//third-party:codespan-reporting", + "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", diff --git a/BUILD b/BUILD index e551d18..782374b 100644 --- a/BUILD +++ b/BUILD @@ -59,6 +59,7 @@ rust_library( deps = [ "//third-party:cc", "//third-party:codespan-reporting", + "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 675261e..3671781 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,6 +13,7 @@ categories = ["development-tools::ffi"] [dependencies] cc = "1.0.49" codespan-reporting = "0.9" +lazy_static = "1.4" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } diff --git a/third-party/BUCK b/third-party/BUCK index e6a5611..3b0ec99 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -36,6 +36,7 @@ rust_library( rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), + visibility = ["PUBLIC"], ) rust_library( diff --git a/third-party/BUILD b/third-party/BUILD index f874d07..3477326 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -41,6 +41,7 @@ rust_library( rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), + visibility = ["//visibility:public"], ) rust_library( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 7e6a175..d975d6e 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -79,6 +79,7 @@ dependencies = [ "cc", "codespan-reporting", "cxx-gen", + "lazy_static", "proc-macro2", "quote", "syn", From e5098cb4a702140427e18f65b3094ca7f9d24e18 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:57:54 +0000 Subject: [PATCH 1002/2232] Expose control of import prefix to Cargo builds --- diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs new file mode 100644 index 0000000..ecc9bfb --- /dev/null +++ b/gen/build/src/cfg.rs @@ -0,0 +1,131 @@ +use std::fmt::{self, Debug}; +use std::marker::PhantomData; + +pub struct Cfg<'a> { + pub include_prefix: &'a str, + marker: PhantomData<*const ()>, // !Send + !Sync +} + +#[cfg(doc)] +pub static mut CFG: Cfg = Cfg { + include_prefix: "", + marker: PhantomData, +}; + +impl<'a> Debug for Cfg<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .debug_struct("Cfg") + .field("include_prefix", &self.include_prefix) + .finish() + } +} + +#[cfg(not(doc))] +pub use self::r#impl::Cfg::CFG; + +#[cfg(not(doc))] +mod r#impl { + use lazy_static::lazy_static; + use std::cell::RefCell; + use std::collections::HashMap; + use std::fmt::{self, Debug}; + use std::marker::PhantomData; + use std::ops::{Deref, DerefMut}; + use std::sync::{PoisonError, RwLock}; + + lazy_static! { + static ref PACKAGE_NAME: Box = { + crate::env_os("CARGO_PKG_NAME") + .map(|pkg| pkg.to_string_lossy().into_owned().into_boxed_str()) + .unwrap_or_default() + }; + static ref INCLUDE_PREFIX: RwLock> = RwLock::new(vec![&PACKAGE_NAME]); + } + + thread_local! { + static CONST_DEREFS: RefCell>>> = RefCell::default(); + } + + #[derive(Eq, PartialEq, Hash)] + struct Handle(*const Cfg<'static>); + + impl<'a> Cfg<'a> { + fn current() -> super::Cfg<'a> { + let include_prefix = *INCLUDE_PREFIX + .read() + .unwrap_or_else(PoisonError::into_inner) + .last() + .unwrap(); + super::Cfg { + include_prefix, + marker: PhantomData, + } + } + + const fn handle(self: &Cfg<'a>) -> Handle { + Handle(<*const Cfg>::cast(self)) + } + } + + // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will + // drop on the same thread where they were created. + pub enum Cfg<'a> { + Mut(super::Cfg<'a>), + CFG, + } + + impl<'a> Debug for Cfg<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let Cfg::Mut(cfg) = self { + Debug::fmt(cfg, formatter) + } else { + Debug::fmt(&Cfg::current(), formatter) + } + } + } + + impl<'a> Deref for Cfg<'a> { + type Target = super::Cfg<'a>; + + fn deref(&self) -> &Self::Target { + if let Cfg::Mut(cfg) = self { + cfg + } else { + let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg { + &mut **derefs + .borrow_mut() + .entry(self.handle()) + .or_insert_with(|| Box::new(Cfg::current())) + }); + unsafe { &mut *cfg } + } + } + } + + impl<'a> DerefMut for Cfg<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + if let Cfg::CFG = self { + CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); + *self = Cfg::Mut(Cfg::current()); + } + match self { + Cfg::Mut(cfg) => cfg, + Cfg::CFG => unreachable!(), + } + } + } + + impl<'a> Drop for Cfg<'a> { + fn drop(&mut self) { + if let Cfg::Mut(cfg) = self { + INCLUDE_PREFIX + .write() + .unwrap_or_else(PoisonError::into_inner) + .push(Box::leak(Box::from(cfg.include_prefix))); + } else { + CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); + } + } + } +} diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 3782543..1e466e5 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -54,6 +54,7 @@ )] mod cargo; +mod cfg; mod error; mod gen; mod out; @@ -73,6 +74,8 @@ use std::iter; use std::path::{Path, PathBuf}; use std::process; +pub use crate::cfg::{Cfg, CFG}; + /// This returns a [`cc::Build`] on which you should continue to set up any /// additional source files or compiler flags, and lastly call its [`compile`] /// method to execute the C++ build. @@ -103,7 +106,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } struct Project { - package_name: OsString, + include_prefix: PathBuf, manifest_dir: PathBuf, // Output directory as received from Cargo. out_dir: PathBuf, @@ -124,7 +127,11 @@ struct Project { impl Project { fn init() -> Result { - let package_name = env_os("CARGO_PKG_NAME")?; + let include_prefix = Path::new(CFG.include_prefix); + assert!(include_prefix.is_relative()); + assert!(!include_prefix.as_os_str().is_empty()); + let include_prefix = include_prefix.components().collect(); + let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; @@ -141,7 +148,7 @@ impl Project { }; Ok(Project { - package_name, + include_prefix, manifest_dir, out_dir, shared_dir, @@ -240,7 +247,7 @@ fn env_include_dirs() -> impl Iterator { fn make_crate_dir(prj: &Project) -> Option { let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); - let link = crate_dir.join(&prj.package_name); + let link = crate_dir.join(&prj.include_prefix); if out::symlink_dir(&prj.manifest_dir, link).is_ok() { println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); Some(crate_dir) @@ -270,8 +277,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let ref rel_path = paths::local_relative_path(rust_source_file); let cxxbridge = prj.out_dir.join("cxxbridge"); - let include_dir = cxxbridge.join("include").join(&prj.package_name); - let sources_dir = cxxbridge.join("sources").join(&prj.package_name); + let include_dir = cxxbridge.join("include").join(&prj.include_prefix); + let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix); let ref rel_path_h = rel_path.with_appended_extension(".h"); let ref header_path = include_dir.join(rel_path_h); @@ -285,8 +292,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); - let shared_h = prj.shared_dir.join(&prj.package_name).join(rel_path_h); - let shared_cc = prj.shared_dir.join(&prj.package_name).join(rel_path_cc); + let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h); + let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc); let _ = out::symlink_file(header_path, shared_h); let _ = out::symlink_file(implementation_path, shared_cc); Ok(()) From d41eef59c96993ff17f929751d3fff5f901ad7b2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 08 2020 23:57:55 +0000 Subject: [PATCH 1003/2232] Change test suite's include_prefix to match workspace path --- diff --git a/tests/BUCK b/tests/BUCK index 47fc557..f066542 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -26,10 +26,9 @@ cxx_library( ":bridge/source", ":module/source", ], - header_namespace = "cxx-test-suite", headers = { - "lib.rs.h": ":bridge/header", - "tests.h": "ffi/tests.h", + "ffi/lib.rs.h": ":bridge/header", + "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], ) diff --git a/tests/BUILD b/tests/BUILD index 68e7be3..1345a7a 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -29,8 +29,6 @@ cc_library( ":module/source", ], hdrs = ["ffi/tests.h"], - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [ ":bridge/include", "//:core", @@ -40,15 +38,11 @@ cc_library( rust_cxx_bridge( name = "bridge", src = "ffi/lib.rs", - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [":impl"], ) rust_cxx_bridge( name = "module", src = "ffi/module.rs", - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [":impl"], ) diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 8042129..e583944 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -1,8 +1,11 @@ +use cxx_build::CFG; + fn main() { if cfg!(trybuild) { return; } + CFG.include_prefix = "tests/ffi"; let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9ace1f2..cc07752 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -23,7 +23,7 @@ pub mod ffi { } extern "C" { - include!("cxx-test-suite/tests.h"); + include!("tests/ffi/tests.h"); type C; diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 8862dc1..77bae06 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -4,7 +4,7 @@ #[cxx::bridge(namespace = tests)] pub mod ffi { extern "C" { - include!("cxx-test-suite/tests.h"); + include!("tests/ffi/tests.h"); type C = crate::ffi::C; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 1f88d8b..9cb6ed0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ -#include "cxx-test-suite/tests.h" -#include "cxx-test-suite/lib.rs.h" +#include "tests/ffi/tests.h" +#include "tests/ffi/lib.rs.h" #include #include #include diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index d4111c6..534f6b5 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -1,12 +1,7 @@ load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@rules_cc//cc:defs.bzl", "cc_library") -def rust_cxx_bridge( - name, - src, - include_prefix = None, - strip_include_prefix = None, - deps = []): +def rust_cxx_bridge(name, src, deps = []): native.alias( name = "%s/header" % name, actual = src + ".h", @@ -43,6 +38,4 @@ def rust_cxx_bridge( cc_library( name = "%s/include" % name, hdrs = [src + ".h"], - include_prefix = include_prefix, - strip_include_prefix = strip_include_prefix, ) From cf3c3f8f5e44a7283601c9648302aa4d4422e899 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 00:23:36 +0000 Subject: [PATCH 1004/2232] Make it possible to have an empty include prefix --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 1e466e5..267b8cf 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -129,7 +129,6 @@ impl Project { fn init() -> Result { let include_prefix = Path::new(CFG.include_prefix); assert!(include_prefix.is_relative()); - assert!(!include_prefix.as_os_str().is_empty()); let include_prefix = include_prefix.components().collect(); let manifest_dir = paths::manifest_dir()?; @@ -246,6 +245,11 @@ fn env_include_dirs() -> impl Iterator { } fn make_crate_dir(prj: &Project) -> Option { + if prj.include_prefix.as_os_str().is_empty() { + let crate_dir = prj.manifest_dir.clone(); + println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); + return Some(crate_dir); + } let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); let link = crate_dir.join(&prj.include_prefix); if out::symlink_dir(&prj.manifest_dir, link).is_ok() { From 983ccf83690d777bda71e4d93703c5227f94457d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 00:36:35 +0000 Subject: [PATCH 1005/2232] Add documentation on CFG and Cfg --- diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs index ecc9bfb..05578df 100644 --- a/gen/build/src/cfg.rs +++ b/gen/build/src/cfg.rs @@ -1,11 +1,64 @@ use std::fmt::{self, Debug}; use std::marker::PhantomData; +/// Build configuration. See [CFG]. pub struct Cfg<'a> { pub include_prefix: &'a str, marker: PhantomData<*const ()>, // !Send + !Sync } +/// Global configuration of the current build. +/// +///
+/// +/// ## **`CFG.include_prefix`** +/// +/// Presently the only exposed configuration is the `include_prefix`, the prefix +/// at which C++ code from your crate as well as directly dependent crates can +/// access the code generated during this build. +/// +/// By default, the `include_prefix` is equal to the name of the current crate. +/// That means if our crate is called `demo` and has Rust source files in a +/// *src/* directory and maybe some handwritten C++ header files in an +/// *include/* directory, then the current crate as well as downstream crates +/// might include them as follows: +/// +/// ``` +/// # const _: &str = stringify! { +/// // include one of the handwritten headers: +/// #include "demo/include/wow.h" +/// +/// // include a header generated from Rust cxx::bridge: +/// #include "demo/src/lib.rs.h" +/// # }; +/// ``` +/// +/// By modifying `CFG.include_prefix` we can substitute a prefix that is +/// different from the crate name if desired. Here we'll change it to +/// `"path/to"` which will make import paths take the form +/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`. +/// +/// ```no_run +/// // build.rs +/// +/// use cxx_build::CFG; +/// +/// fn main() { +/// CFG.include_prefix = "path/to"; +/// +/// cxx_build::bridge("src/lib.rs") +/// .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"` +/// /* ... */ +/// .compile("demo"); +/// } +/// ``` +/// +/// Note that cross-crate imports are only made available between **direct +/// dependencies**. Another crate must directly depend on your crate in order to +/// #include its headers; a transitive dependency is not sufficient. +/// Additionally, headers from a direct dependency are only importable if the +/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers +/// will not be importable from outside of the same crate. #[cfg(doc)] pub static mut CFG: Cfg = Cfg { include_prefix: "", From 40299e2ccd4d4e96945092fc0c9a7b5c4ceb30c0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 01:01:29 +0000 Subject: [PATCH 1006/2232] Merge pull request #347 from dtolnay/cfg Expose control of import prefix to Cargo builds --- diff --git a/BUCK b/BUCK index 0505bd2..22db155 100644 --- a/BUCK +++ b/BUCK @@ -53,6 +53,7 @@ rust_library( deps = [ "//third-party:cc", "//third-party:codespan-reporting", + "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", diff --git a/BUILD b/BUILD index e551d18..782374b 100644 --- a/BUILD +++ b/BUILD @@ -59,6 +59,7 @@ rust_library( deps = [ "//third-party:cc", "//third-party:codespan-reporting", + "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 675261e..3671781 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,6 +13,7 @@ categories = ["development-tools::ffi"] [dependencies] cc = "1.0.49" codespan-reporting = "0.9" +lazy_static = "1.4" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs new file mode 100644 index 0000000..05578df --- /dev/null +++ b/gen/build/src/cfg.rs @@ -0,0 +1,184 @@ +use std::fmt::{self, Debug}; +use std::marker::PhantomData; + +/// Build configuration. See [CFG]. +pub struct Cfg<'a> { + pub include_prefix: &'a str, + marker: PhantomData<*const ()>, // !Send + !Sync +} + +/// Global configuration of the current build. +/// +///
+/// +/// ## **`CFG.include_prefix`** +/// +/// Presently the only exposed configuration is the `include_prefix`, the prefix +/// at which C++ code from your crate as well as directly dependent crates can +/// access the code generated during this build. +/// +/// By default, the `include_prefix` is equal to the name of the current crate. +/// That means if our crate is called `demo` and has Rust source files in a +/// *src/* directory and maybe some handwritten C++ header files in an +/// *include/* directory, then the current crate as well as downstream crates +/// might include them as follows: +/// +/// ``` +/// # const _: &str = stringify! { +/// // include one of the handwritten headers: +/// #include "demo/include/wow.h" +/// +/// // include a header generated from Rust cxx::bridge: +/// #include "demo/src/lib.rs.h" +/// # }; +/// ``` +/// +/// By modifying `CFG.include_prefix` we can substitute a prefix that is +/// different from the crate name if desired. Here we'll change it to +/// `"path/to"` which will make import paths take the form +/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`. +/// +/// ```no_run +/// // build.rs +/// +/// use cxx_build::CFG; +/// +/// fn main() { +/// CFG.include_prefix = "path/to"; +/// +/// cxx_build::bridge("src/lib.rs") +/// .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"` +/// /* ... */ +/// .compile("demo"); +/// } +/// ``` +/// +/// Note that cross-crate imports are only made available between **direct +/// dependencies**. Another crate must directly depend on your crate in order to +/// #include its headers; a transitive dependency is not sufficient. +/// Additionally, headers from a direct dependency are only importable if the +/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers +/// will not be importable from outside of the same crate. +#[cfg(doc)] +pub static mut CFG: Cfg = Cfg { + include_prefix: "", + marker: PhantomData, +}; + +impl<'a> Debug for Cfg<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .debug_struct("Cfg") + .field("include_prefix", &self.include_prefix) + .finish() + } +} + +#[cfg(not(doc))] +pub use self::r#impl::Cfg::CFG; + +#[cfg(not(doc))] +mod r#impl { + use lazy_static::lazy_static; + use std::cell::RefCell; + use std::collections::HashMap; + use std::fmt::{self, Debug}; + use std::marker::PhantomData; + use std::ops::{Deref, DerefMut}; + use std::sync::{PoisonError, RwLock}; + + lazy_static! { + static ref PACKAGE_NAME: Box = { + crate::env_os("CARGO_PKG_NAME") + .map(|pkg| pkg.to_string_lossy().into_owned().into_boxed_str()) + .unwrap_or_default() + }; + static ref INCLUDE_PREFIX: RwLock> = RwLock::new(vec![&PACKAGE_NAME]); + } + + thread_local! { + static CONST_DEREFS: RefCell>>> = RefCell::default(); + } + + #[derive(Eq, PartialEq, Hash)] + struct Handle(*const Cfg<'static>); + + impl<'a> Cfg<'a> { + fn current() -> super::Cfg<'a> { + let include_prefix = *INCLUDE_PREFIX + .read() + .unwrap_or_else(PoisonError::into_inner) + .last() + .unwrap(); + super::Cfg { + include_prefix, + marker: PhantomData, + } + } + + const fn handle(self: &Cfg<'a>) -> Handle { + Handle(<*const Cfg>::cast(self)) + } + } + + // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will + // drop on the same thread where they were created. + pub enum Cfg<'a> { + Mut(super::Cfg<'a>), + CFG, + } + + impl<'a> Debug for Cfg<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let Cfg::Mut(cfg) = self { + Debug::fmt(cfg, formatter) + } else { + Debug::fmt(&Cfg::current(), formatter) + } + } + } + + impl<'a> Deref for Cfg<'a> { + type Target = super::Cfg<'a>; + + fn deref(&self) -> &Self::Target { + if let Cfg::Mut(cfg) = self { + cfg + } else { + let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg { + &mut **derefs + .borrow_mut() + .entry(self.handle()) + .or_insert_with(|| Box::new(Cfg::current())) + }); + unsafe { &mut *cfg } + } + } + } + + impl<'a> DerefMut for Cfg<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + if let Cfg::CFG = self { + CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); + *self = Cfg::Mut(Cfg::current()); + } + match self { + Cfg::Mut(cfg) => cfg, + Cfg::CFG => unreachable!(), + } + } + } + + impl<'a> Drop for Cfg<'a> { + fn drop(&mut self) { + if let Cfg::Mut(cfg) = self { + INCLUDE_PREFIX + .write() + .unwrap_or_else(PoisonError::into_inner) + .push(Box::leak(Box::from(cfg.include_prefix))); + } else { + CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); + } + } + } +} diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 3782543..267b8cf 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -54,6 +54,7 @@ )] mod cargo; +mod cfg; mod error; mod gen; mod out; @@ -73,6 +74,8 @@ use std::iter; use std::path::{Path, PathBuf}; use std::process; +pub use crate::cfg::{Cfg, CFG}; + /// This returns a [`cc::Build`] on which you should continue to set up any /// additional source files or compiler flags, and lastly call its [`compile`] /// method to execute the C++ build. @@ -103,7 +106,7 @@ pub fn bridges(rust_source_files: impl IntoIterator>) -> } struct Project { - package_name: OsString, + include_prefix: PathBuf, manifest_dir: PathBuf, // Output directory as received from Cargo. out_dir: PathBuf, @@ -124,7 +127,10 @@ struct Project { impl Project { fn init() -> Result { - let package_name = env_os("CARGO_PKG_NAME")?; + let include_prefix = Path::new(CFG.include_prefix); + assert!(include_prefix.is_relative()); + let include_prefix = include_prefix.components().collect(); + let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; @@ -141,7 +147,7 @@ impl Project { }; Ok(Project { - package_name, + include_prefix, manifest_dir, out_dir, shared_dir, @@ -239,8 +245,13 @@ fn env_include_dirs() -> impl Iterator { } fn make_crate_dir(prj: &Project) -> Option { + if prj.include_prefix.as_os_str().is_empty() { + let crate_dir = prj.manifest_dir.clone(); + println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); + return Some(crate_dir); + } let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); - let link = crate_dir.join(&prj.package_name); + let link = crate_dir.join(&prj.include_prefix); if out::symlink_dir(&prj.manifest_dir, link).is_ok() { println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy()); Some(crate_dir) @@ -270,8 +281,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> let ref rel_path = paths::local_relative_path(rust_source_file); let cxxbridge = prj.out_dir.join("cxxbridge"); - let include_dir = cxxbridge.join("include").join(&prj.package_name); - let sources_dir = cxxbridge.join("sources").join(&prj.package_name); + let include_dir = cxxbridge.join("include").join(&prj.include_prefix); + let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix); let ref rel_path_h = rel_path.with_appended_extension(".h"); let ref header_path = include_dir.join(rel_path_h); @@ -285,8 +296,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); - let shared_h = prj.shared_dir.join(&prj.package_name).join(rel_path_h); - let shared_cc = prj.shared_dir.join(&prj.package_name).join(rel_path_cc); + let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h); + let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc); let _ = out::symlink_file(header_path, shared_h); let _ = out::symlink_file(implementation_path, shared_cc); Ok(()) diff --git a/tests/BUCK b/tests/BUCK index 47fc557..f066542 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -26,10 +26,9 @@ cxx_library( ":bridge/source", ":module/source", ], - header_namespace = "cxx-test-suite", headers = { - "lib.rs.h": ":bridge/header", - "tests.h": "ffi/tests.h", + "ffi/lib.rs.h": ":bridge/header", + "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], ) diff --git a/tests/BUILD b/tests/BUILD index 68e7be3..1345a7a 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -29,8 +29,6 @@ cc_library( ":module/source", ], hdrs = ["ffi/tests.h"], - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [ ":bridge/include", "//:core", @@ -40,15 +38,11 @@ cc_library( rust_cxx_bridge( name = "bridge", src = "ffi/lib.rs", - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [":impl"], ) rust_cxx_bridge( name = "module", src = "ffi/module.rs", - include_prefix = "cxx-test-suite", - strip_include_prefix = "ffi", deps = [":impl"], ) diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 8042129..e583944 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -1,8 +1,11 @@ +use cxx_build::CFG; + fn main() { if cfg!(trybuild) { return; } + CFG.include_prefix = "tests/ffi"; let sources = vec!["lib.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9ace1f2..cc07752 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -23,7 +23,7 @@ pub mod ffi { } extern "C" { - include!("cxx-test-suite/tests.h"); + include!("tests/ffi/tests.h"); type C; diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 8862dc1..77bae06 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -4,7 +4,7 @@ #[cxx::bridge(namespace = tests)] pub mod ffi { extern "C" { - include!("cxx-test-suite/tests.h"); + include!("tests/ffi/tests.h"); type C = crate::ffi::C; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 1f88d8b..9cb6ed0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,5 +1,5 @@ -#include "cxx-test-suite/tests.h" -#include "cxx-test-suite/lib.rs.h" +#include "tests/ffi/tests.h" +#include "tests/ffi/lib.rs.h" #include #include #include diff --git a/third-party/BUCK b/third-party/BUCK index e6a5611..3b0ec99 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -36,6 +36,7 @@ rust_library( rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), + visibility = ["PUBLIC"], ) rust_library( diff --git a/third-party/BUILD b/third-party/BUILD index f874d07..3477326 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -41,6 +41,7 @@ rust_library( rust_library( name = "lazy_static", srcs = glob(["vendor/lazy_static-1.4.0/src/**"]), + visibility = ["//visibility:public"], ) rust_library( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 7e6a175..d975d6e 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -79,6 +79,7 @@ dependencies = [ "cc", "codespan-reporting", "cxx-gen", + "lazy_static", "proc-macro2", "quote", "syn", diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl index d4111c6..534f6b5 100644 --- a/tools/bazel/rust_cxx_bridge.bzl +++ b/tools/bazel/rust_cxx_bridge.bzl @@ -1,12 +1,7 @@ load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@rules_cc//cc:defs.bzl", "cc_library") -def rust_cxx_bridge( - name, - src, - include_prefix = None, - strip_include_prefix = None, - deps = []): +def rust_cxx_bridge(name, src, deps = []): native.alias( name = "%s/header" % name, actual = src + ".h", @@ -43,6 +38,4 @@ def rust_cxx_bridge( cc_library( name = "%s/include" % name, hdrs = [src + ".h"], - include_prefix = include_prefix, - strip_include_prefix = strip_include_prefix, ) From 039320287d0ace5cf3ae776a361c17d4a6defb71 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 02:25:06 +0000 Subject: [PATCH 1007/2232] Add fixme to remove const derefs side table --- diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs index 05578df..e0186a3 100644 --- a/gen/build/src/cfg.rs +++ b/gen/build/src/cfg.rs @@ -97,6 +97,18 @@ mod r#impl { } thread_local! { + // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved, + // we can delete this thread local side table and instead make each CFG + // instance directly own the associated super::Cfg. + // + // #[allow(const_item_mutation)] + // pub const CFG: Cfg = Cfg { + // cfg: AtomicPtr::new(ptr::null_mut()), + // }; + // pub struct Cfg { + // cfg: AtomicPtr, + // } + // static CONST_DEREFS: RefCell>>> = RefCell::default(); } From a8beeef131270233940cb5e34bf63b05e886d352 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 02:29:21 +0000 Subject: [PATCH 1008/2232] Import scratch crate --- diff --git a/BUCK b/BUCK index 22db155..69b641e 100644 --- a/BUCK +++ b/BUCK @@ -56,6 +56,7 @@ rust_library( "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", + "//third-party:scratch", "//third-party:syn", ], ) diff --git a/BUILD b/BUILD index 782374b..3ada4bc 100644 --- a/BUILD +++ b/BUILD @@ -62,6 +62,7 @@ rust_library( "//third-party:lazy_static", "//third-party:proc-macro2", "//third-party:quote", + "//third-party:scratch", "//third-party:syn", ], ) diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 3671781..bf87f1f 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -16,6 +16,7 @@ codespan-reporting = "0.9" lazy_static = "1.4" proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } +scratch = "1.0" syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [dev-dependencies] diff --git a/third-party/BUCK b/third-party/BUCK index 3b0ec99..afc696b 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -64,6 +64,13 @@ rust_library( ) rust_library( + name = "scratch", + srcs = glob(["vendor/scratch-1.0.0/src/**"]), + env = {"OUT_DIR": ""}, + visibility = ["PUBLIC"], +) + +rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.42/src/**"]), visibility = ["PUBLIC"], diff --git a/third-party/BUILD b/third-party/BUILD index 3477326..49bf512 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -69,6 +69,13 @@ rust_library( ) rust_library( + name = "scratch", + srcs = glob(["vendor/scratch-1.0.0/src/**"]), + rustc_env = {"OUT_DIR": ""}, + visibility = ["//visibility:public"], +) + +rust_library( name = "syn", srcs = glob(["vendor/syn-1.0.42/src/**"]), crate_features = [ diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index d975d6e..c11e27e 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -82,6 +82,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", + "scratch", "syn", ] @@ -222,6 +223,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] +name = "scratch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" + +[[package]] name = "serde" version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" From 66ebdd1d1fecf1fe095aef6d0636dda580d5210e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 02:31:48 +0000 Subject: [PATCH 1009/2232] Move fallback to scratch crate's out dir --- diff --git a/BUCK b/BUCK index 69b641e..da6fcb2 100644 --- a/BUCK +++ b/BUCK @@ -48,7 +48,6 @@ rust_library( rust_library( name = "build", srcs = glob(["gen/build/src/**"]), - env = {"OUT_DIR": ""}, visibility = ["PUBLIC"], deps = [ "//third-party:cc", diff --git a/BUILD b/BUILD index 3ada4bc..24a1c8b 100644 --- a/BUILD +++ b/BUILD @@ -54,7 +54,6 @@ rust_library( name = "build", srcs = glob(["gen/build/src/**/*.rs"]), data = ["gen/build/src/gen/include/cxx.h"], - rustc_env = {"OUT_DIR": ""}, visibility = ["//visibility:public"], deps = [ "//third-party:cc", diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 267b8cf..135833b 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -142,8 +142,7 @@ impl Project { let shared_dir = match target_dir { TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), - // Use cxx-build's OUT_DIR. - TargetDir::Unknown => PathBuf::from(env!("OUT_DIR")), + TargetDir::Unknown => scratch::path("cxxbridge"), }; Ok(Project { From 8f16ae75f3fc84be1e17bc66d267f4aa2385ea7e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 02:34:50 +0000 Subject: [PATCH 1010/2232] Bump namespace to 05 --- diff --git a/Cargo.toml b/Cargo.toml index d8b93f1..54c968f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "cxx" version = "0.4.7" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" -links = "cxxbridge04" +links = "cxxbridge05" license = "MIT OR Apache-2.0" description = "Safe interop between Rust and C++" repository = "https://github.com/dtolnay/cxx" diff --git a/build.rs b/build.rs index 688b5b7..57b3e52 100644 --- a/build.rs +++ b/build.rs @@ -7,7 +7,7 @@ fn main() { .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate .flag_if_supported(cxxbridge_flags::STD) - .compile("cxxbridge04"); + .compile("cxxbridge05"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); println!("cargo:rustc-cfg=built_with_cargo"); diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 135833b..7148d7b 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -263,7 +263,7 @@ fn make_include_dir(prj: &Project) -> Result { let include_dir = prj.out_dir.join("cxxbridge").join("include"); let cxx_h = include_dir.join("rust").join("cxx.h"); let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); - if let Some(ref original) = env::var_os("DEP_CXXBRIDGE04_HEADER") { + if let Some(ref original) = env::var_os("DEP_CXXBRIDGE05_HEADER") { out::symlink_file(original, cxx_h)?; out::symlink_file(original, shared_cxx_h)?; } else { diff --git a/gen/src/write.rs b/gen/src/write.rs index 1048272..9e6a297 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -245,7 +245,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge04"); + out.begin_block("inline namespace cxxbridge05"); if needs_panic || needs_rust_string @@ -263,22 +263,22 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "// #include \"rust/cxx.h\""); } - include::write(out, needs_panic, "CXXBRIDGE04_PANIC"); + include::write(out, needs_panic, "CXXBRIDGE05_PANIC"); if needs_rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } - include::write(out, needs_rust_string, "CXXBRIDGE04_RUST_STRING"); - include::write(out, needs_rust_str, "CXXBRIDGE04_RUST_STR"); - include::write(out, needs_rust_slice, "CXXBRIDGE04_RUST_SLICE"); - include::write(out, needs_rust_box, "CXXBRIDGE04_RUST_BOX"); - include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE04_RUST_BITCOPY"); - include::write(out, needs_rust_vec, "CXXBRIDGE04_RUST_VEC"); - include::write(out, needs_rust_fn, "CXXBRIDGE04_RUST_FN"); - include::write(out, needs_rust_error, "CXXBRIDGE04_RUST_ERROR"); - include::write(out, needs_rust_isize, "CXXBRIDGE04_RUST_ISIZE"); + include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING"); + include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR"); + include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE"); + include::write(out, needs_rust_box, "CXXBRIDGE05_RUST_BOX"); + include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + include::write(out, needs_rust_vec, "CXXBRIDGE05_RUST_VEC"); + include::write(out, needs_rust_fn, "CXXBRIDGE05_RUST_FN"); + include::write(out, needs_rust_error, "CXXBRIDGE05_RUST_ERROR"); + include::write(out, needs_rust_isize, "CXXBRIDGE05_RUST_ISIZE"); if needs_manually_drop { out.next_section(); @@ -304,7 +304,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { writeln!(out, "}};"); } - out.end_block("namespace cxxbridge04"); + out.end_block("namespace cxxbridge05"); if needs_trycatch { out.begin_block("namespace behavior"); @@ -333,7 +333,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE04_STRUCT_{}{}", out.namespace, strct.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, strct.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { @@ -358,7 +358,7 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) { } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE04_STRUCT_{}{}", out.namespace, ety.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, ety.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { @@ -379,7 +379,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE04_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE05_ENUM_{}{}", out.namespace, enm.ident); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { @@ -465,7 +465,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { out.next_section(); writeln!( out, - "const char *cxxbridge04$exception(const char *, size_t);", + "const char *cxxbridge05$exception(const char *, size_t);", ); } } @@ -610,7 +610,7 @@ fn write_cxx_function_shim( writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge04$exception(catch$, throw$.len);", + " throw$.ptr = cxxbridge05$exception(catch$, throw$.len);", ); writeln!(out, " }});"); writeln!(out, " return throw$;"); @@ -1089,7 +1089,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("extern \"C\""); out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge04"); + out.begin_block("inline namespace cxxbridge05"); for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1103,7 +1103,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } - out.end_block("namespace cxxbridge04"); + out.end_block("namespace cxxbridge05"); out.end_block("namespace rust"); } @@ -1116,19 +1116,19 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { inner += &ident.to_string(); let instance = inner.replace("::", "$"); - writeln!(out, "#ifndef CXXBRIDGE04_RUST_BOX_{}", instance); - writeln!(out, "#define CXXBRIDGE04_RUST_BOX_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); + writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); writeln!( out, - "void cxxbridge04$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge05$box${}$uninit(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge04$box${}$drop(::rust::Box<{}> *ptr) noexcept;", + "void cxxbridge05$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); - writeln!(out, "#endif // CXXBRIDGE04_RUST_BOX_{}", instance); + writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { @@ -1136,34 +1136,34 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE04_RUST_VEC_{}", instance); - writeln!(out, "#define CXXBRIDGE04_RUST_VEC_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); + writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); writeln!( out, - "void cxxbridge04$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge05$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "void cxxbridge04$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", + "void cxxbridge05$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "size_t cxxbridge04$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", + "size_t cxxbridge05$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, - "const {} *cxxbridge04$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", + "const {} *cxxbridge05$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;", inner, instance, ); writeln!( out, - "size_t cxxbridge04$rust_vec${}$stride() noexcept;", + "size_t cxxbridge05$rust_vec${}$stride() noexcept;", instance, ); - writeln!(out, "#endif // CXXBRIDGE04_RUST_VEC_{}", instance); + writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { @@ -1177,12 +1177,12 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); - writeln!(out, " cxxbridge04$box${}$uninit(this);", instance); + writeln!(out, " cxxbridge05$box${}$uninit(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); - writeln!(out, " cxxbridge04$box${}$drop(this);", instance); + writeln!(out, " cxxbridge05$box${}$drop(this);", instance); writeln!(out, "}}"); } @@ -1193,35 +1193,35 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); - writeln!(out, " cxxbridge04$rust_vec${}$new(this);", instance); + writeln!(out, " cxxbridge05$rust_vec${}$new(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!( out, - " return cxxbridge04$rust_vec${}$drop(this);", + " return cxxbridge05$rust_vec${}$drop(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner); - writeln!(out, " return cxxbridge04$rust_vec${}$len(this);", instance); + writeln!(out, " return cxxbridge05$rust_vec${}$len(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner); writeln!( out, - " return cxxbridge04$rust_vec${}$data(this);", + " return cxxbridge05$rust_vec${}$data(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner); - writeln!(out, " return cxxbridge04$rust_vec${}$stride();", instance); + writeln!(out, " return cxxbridge05$rust_vec${}$stride();", instance); writeln!(out, "}}"); } @@ -1229,12 +1229,12 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { let ty = Type::Ident(ident.clone()); let instance = to_mangled(&out.namespace, &ty); - writeln!(out, "#ifndef CXXBRIDGE04_UNIQUE_PTR_{}", instance); - writeln!(out, "#define CXXBRIDGE04_UNIQUE_PTR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); + writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); write_unique_ptr_common(out, &ty, types); - writeln!(out, "#endif // CXXBRIDGE04_UNIQUE_PTR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE05_UNIQUE_PTR_{}", instance); } // Shared by UniquePtr and UniquePtr>. @@ -1261,7 +1261,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { ); writeln!( out, - "void cxxbridge04$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge05$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner); @@ -1269,7 +1269,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { if can_construct_from_value { writeln!( out, - "void cxxbridge04$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", + "void cxxbridge05$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{", instance, inner, inner, ); writeln!( @@ -1281,28 +1281,28 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { } writeln!( out, - "void cxxbridge04$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", + "void cxxbridge05$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{", instance, inner, inner, ); writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge04$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", + "const {} *cxxbridge05$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); writeln!( out, - "{} *cxxbridge04$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", + "{} *cxxbridge05$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); writeln!( out, - "void cxxbridge04$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", + "void cxxbridge05$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ptr->~unique_ptr();"); @@ -1314,18 +1314,18 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: let inner = to_typename(&out.namespace, &element); let instance = to_mangled(&out.namespace, &element); - writeln!(out, "#ifndef CXXBRIDGE04_VECTOR_{}", instance); - writeln!(out, "#define CXXBRIDGE04_VECTOR_{}", instance); + writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); + writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); writeln!( out, - "size_t cxxbridge04$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", + "size_t cxxbridge05$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); writeln!(out, "}}"); writeln!( out, - "const {} *cxxbridge04$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", + "const {} *cxxbridge05$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{", inner, instance, inner, ); writeln!(out, " return &s[pos];"); @@ -1333,5 +1333,5 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: write_unique_ptr_common(out, vector_ty, types); - writeln!(out, "#endif // CXXBRIDGE04_VECTOR_{}", instance); + writeln!(out, "#endif // CXXBRIDGE05_VECTOR_{}", instance); } diff --git a/include/cxx.h b/include/cxx.h index b2e0eee..0373a38 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -15,12 +15,12 @@ #endif namespace rust { -inline namespace cxxbridge04 { +inline namespace cxxbridge05 { struct unsafe_bitcopy_t; -#ifndef CXXBRIDGE04_RUST_STRING -#define CXXBRIDGE04_RUST_STRING +#ifndef CXXBRIDGE05_RUST_STRING +#define CXXBRIDGE05_RUST_STRING class String final { public: String() noexcept; @@ -49,10 +49,10 @@ private: // Size and alignment statically verified by rust_string.rs. std::array repr; }; -#endif // CXXBRIDGE04_RUST_STRING +#endif // CXXBRIDGE05_RUST_STRING -#ifndef CXXBRIDGE04_RUST_STR -#define CXXBRIDGE04_RUST_STR +#ifndef CXXBRIDGE05_RUST_STR +#define CXXBRIDGE05_RUST_STR class Str final { public: Str() noexcept; @@ -86,9 +86,9 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE04_RUST_STR +#endif // CXXBRIDGE05_RUST_STR -#ifndef CXXBRIDGE04_RUST_SLICE +#ifndef CXXBRIDGE05_RUST_SLICE template class Slice final { public: @@ -117,9 +117,9 @@ public: private: Repr repr; }; -#endif // CXXBRIDGE04_RUST_SLICE +#endif // CXXBRIDGE05_RUST_SLICE -#ifndef CXXBRIDGE04_RUST_BOX +#ifndef CXXBRIDGE05_RUST_BOX template class Box final { public: @@ -158,9 +158,9 @@ private: void drop() noexcept; T *ptr; }; -#endif // CXXBRIDGE04_RUST_BOX +#endif // CXXBRIDGE05_RUST_BOX -#ifndef CXXBRIDGE04_RUST_VEC +#ifndef CXXBRIDGE05_RUST_VEC template class Vec final { public: @@ -218,10 +218,10 @@ private: // Size and alignment statically verified by rust_vec.rs. std::array repr; }; -#endif // CXXBRIDGE04_RUST_VEC +#endif // CXXBRIDGE05_RUST_VEC -#ifndef CXXBRIDGE04_RUST_FN -#define CXXBRIDGE04_RUST_FN +#ifndef CXXBRIDGE05_RUST_FN +#define CXXBRIDGE05_RUST_FN template class Fn; @@ -238,10 +238,10 @@ private: template using TryFn = Fn; -#endif // CXXBRIDGE04_RUST_FN +#endif // CXXBRIDGE05_RUST_FN -#ifndef CXXBRIDGE04_RUST_ERROR -#define CXXBRIDGE04_RUST_ERROR +#ifndef CXXBRIDGE05_RUST_ERROR +#define CXXBRIDGE05_RUST_ERROR class Error final : public std::exception { public: Error(const Error &); @@ -253,16 +253,16 @@ public: private: Str::Repr msg; }; -#endif // CXXBRIDGE04_RUST_ERROR +#endif // CXXBRIDGE05_RUST_ERROR -#ifndef CXXBRIDGE04_RUST_ISIZE -#define CXXBRIDGE04_RUST_ISIZE +#ifndef CXXBRIDGE05_RUST_ISIZE +#define CXXBRIDGE05_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif -#endif // CXXBRIDGE04_RUST_ISIZE +#endif // CXXBRIDGE05_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -287,11 +287,11 @@ using try_fn = TryFn; //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details -#ifndef CXXBRIDGE04_PANIC -#define CXXBRIDGE04_PANIC +#ifndef CXXBRIDGE05_PANIC +#define CXXBRIDGE05_PANIC template void panic [[noreturn]] (const char *msg); -#endif // CXXBRIDGE04_PANIC +#endif // CXXBRIDGE05_PANIC template Ret Fn::operator()(Args... args) const noexcept(!Throws) { @@ -303,17 +303,17 @@ Fn Fn::operator*() const noexcept { return *this; } -#ifndef CXXBRIDGE04_RUST_BITCOPY -#define CXXBRIDGE04_RUST_BITCOPY +#ifndef CXXBRIDGE05_RUST_BITCOPY +#define CXXBRIDGE05_RUST_BITCOPY struct unsafe_bitcopy_t { explicit unsafe_bitcopy_t() = default; }; constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE04_RUST_BITCOPY +#endif // CXXBRIDGE05_RUST_BITCOPY -#ifndef CXXBRIDGE04_RUST_SLICE -#define CXXBRIDGE04_RUST_SLICE +#ifndef CXXBRIDGE05_RUST_SLICE +#define CXXBRIDGE05_RUST_SLICE template Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} @@ -351,10 +351,10 @@ template Slice::operator Repr() noexcept { return this->repr; } -#endif // CXXBRIDGE04_RUST_SLICE +#endif // CXXBRIDGE05_RUST_SLICE -#ifndef CXXBRIDGE04_RUST_BOX -#define CXXBRIDGE04_RUST_BOX +#ifndef CXXBRIDGE05_RUST_BOX +#define CXXBRIDGE05_RUST_BOX template Box::Box(const Box &other) : Box(*other) {} @@ -450,10 +450,10 @@ T *Box::into_raw() noexcept { template Box::Box() noexcept {} -#endif // CXXBRIDGE04_RUST_BOX +#endif // CXXBRIDGE05_RUST_BOX -#ifndef CXXBRIDGE04_RUST_VEC -#define CXXBRIDGE04_RUST_VEC +#ifndef CXXBRIDGE05_RUST_VEC +#define CXXBRIDGE05_RUST_VEC template Vec::Vec(Vec &&other) noexcept { this->repr = other.repr; @@ -558,7 +558,7 @@ typename Vec::const_iterator Vec::end() const noexcept { // Internal API only intended for the cxxbridge code generator. template Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} -#endif // CXXBRIDGE04_RUST_VEC +#endif // CXXBRIDGE05_RUST_VEC -} // namespace cxxbridge04 +} // namespace cxxbridge05 } // namespace rust diff --git a/macro/src/expand.rs b/macro/src/expand.rs index dd5350f..20f166f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -727,7 +727,7 @@ fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge04$box${}{}$", namespace, ident); + let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -756,7 +756,7 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge04$rust_vec${}{}$", namespace, elem); + let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -807,7 +807,7 @@ fn expand_unique_ptr( explicit_impl: Option<&Impl>, ) -> TokenStream { let name = ident.to_string(); - let prefix = format!("cxxbridge04$unique_ptr${}{}$", namespace, ident); + let prefix = format!("cxxbridge05$unique_ptr${}{}$", namespace, ident); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -890,10 +890,10 @@ fn expand_cxx_vector( ) -> TokenStream { let _ = explicit_impl; let name = elem.to_string(); - let prefix = format!("cxxbridge04$std$vector${}{}$", namespace, elem); + let prefix = format!("cxxbridge05$std$vector${}{}$", namespace, elem); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge04$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}{}$", namespace, elem); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/src/cxx.cc b/src/cxx.cc index cd75162..715e0ce 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -7,30 +7,30 @@ #include extern "C" { -const char *cxxbridge04$cxx_string$data(const std::string &s) noexcept { +const char *cxxbridge05$cxx_string$data(const std::string &s) noexcept { return s.data(); } -size_t cxxbridge04$cxx_string$length(const std::string &s) noexcept { +size_t cxxbridge05$cxx_string$length(const std::string &s) noexcept { return s.length(); } // rust::String -void cxxbridge04$string$new(rust::String *self) noexcept; -void cxxbridge04$string$clone(rust::String *self, +void cxxbridge05$string$new(rust::String *self) noexcept; +void cxxbridge05$string$clone(rust::String *self, const rust::String &other) noexcept; -bool cxxbridge04$string$from(rust::String *self, const char *ptr, +bool cxxbridge05$string$from(rust::String *self, const char *ptr, size_t len) noexcept; -void cxxbridge04$string$drop(rust::String *self) noexcept; -const char *cxxbridge04$string$ptr(const rust::String *self) noexcept; -size_t cxxbridge04$string$len(const rust::String *self) noexcept; +void cxxbridge05$string$drop(rust::String *self) noexcept; +const char *cxxbridge05$string$ptr(const rust::String *self) noexcept; +size_t cxxbridge05$string$len(const rust::String *self) noexcept; // rust::Str -bool cxxbridge04$str$valid(const char *ptr, size_t len) noexcept; +bool cxxbridge05$str$valid(const char *ptr, size_t len) noexcept; } // extern "C" namespace rust { -inline namespace cxxbridge04 { +inline namespace cxxbridge05 { template void panic [[noreturn]] (const char *msg) { @@ -44,42 +44,42 @@ void panic [[noreturn]] (const char *msg) { template void panic[[noreturn]] (const char *msg); -String::String() noexcept { cxxbridge04$string$new(this); } +String::String() noexcept { cxxbridge05$string$new(this); } String::String(const String &other) noexcept { - cxxbridge04$string$clone(this, other); + cxxbridge05$string$clone(this, other); } String::String(String &&other) noexcept { this->repr = other.repr; - cxxbridge04$string$new(&other); + cxxbridge05$string$new(&other); } -String::~String() noexcept { cxxbridge04$string$drop(this); } +String::~String() noexcept { cxxbridge05$string$drop(this); } String::String(const std::string &s) : String(s.data(), s.length()) {} String::String(const char *s) : String(s, std::strlen(s)) {} String::String(const char *s, size_t len) { - if (!cxxbridge04$string$from(this, s, len)) { + if (!cxxbridge05$string$from(this, s, len)) { panic("data for rust::String is not utf-8"); } } String &String::operator=(const String &other) noexcept { if (this != &other) { - cxxbridge04$string$drop(this); - cxxbridge04$string$clone(this, other); + cxxbridge05$string$drop(this); + cxxbridge05$string$clone(this, other); } return *this; } String &String::operator=(String &&other) noexcept { if (this != &other) { - cxxbridge04$string$drop(this); + cxxbridge05$string$drop(this); this->repr = other.repr; - cxxbridge04$string$new(&other); + cxxbridge05$string$new(&other); } return *this; } @@ -89,12 +89,12 @@ String::operator std::string() const { } const char *String::data() const noexcept { - return cxxbridge04$string$ptr(this); + return cxxbridge05$string$ptr(this); } -size_t String::size() const noexcept { return cxxbridge04$string$len(this); } +size_t String::size() const noexcept { return cxxbridge05$string$len(this); } -size_t String::length() const noexcept { return cxxbridge04$string$len(this); } +size_t String::length() const noexcept { return cxxbridge05$string$len(this); } String::String(unsafe_bitcopy_t, const String &bits) noexcept : repr(bits.repr) {} @@ -113,7 +113,7 @@ Str::Str(const std::string &s) : Str(s.data(), s.length()) {} Str::Str(const char *s) : Str(s, std::strlen(s)) {} Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { - if (!cxxbridge04$str$valid(this->repr.ptr, this->repr.len)) { + if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } } @@ -143,7 +143,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { } extern "C" { -const char *cxxbridge04$error(const char *ptr, size_t len) { +const char *cxxbridge05$error(const char *ptr, size_t len) { char *copy = new char[len]; strncpy(copy, ptr, len); return copy; @@ -153,7 +153,7 @@ const char *cxxbridge04$error(const char *ptr, size_t len) { Error::Error(Str::Repr msg) noexcept : msg(msg) {} Error::Error(const Error &other) { - this->msg.ptr = cxxbridge04$error(other.msg.ptr, other.msg.len); + this->msg.ptr = cxxbridge05$error(other.msg.ptr, other.msg.len); this->msg.len = other.msg.len; } @@ -168,96 +168,96 @@ Error::~Error() noexcept { delete[] this->msg.ptr; } const char *Error::what() const noexcept { return this->msg.ptr; } -} // namespace cxxbridge04 +} // namespace cxxbridge05 } // namespace rust extern "C" { -void cxxbridge04$unique_ptr$std$string$null( +void cxxbridge05$unique_ptr$std$string$null( std::unique_ptr *ptr) noexcept { new (ptr) std::unique_ptr(); } -void cxxbridge04$unique_ptr$std$string$raw(std::unique_ptr *ptr, +void cxxbridge05$unique_ptr$std$string$raw(std::unique_ptr *ptr, std::string *raw) noexcept { new (ptr) std::unique_ptr(raw); } -const std::string *cxxbridge04$unique_ptr$std$string$get( +const std::string *cxxbridge05$unique_ptr$std$string$get( const std::unique_ptr &ptr) noexcept { return ptr.get(); } -std::string *cxxbridge04$unique_ptr$std$string$release( +std::string *cxxbridge05$unique_ptr$std$string$release( std::unique_ptr &ptr) noexcept { return ptr.release(); } -void cxxbridge04$unique_ptr$std$string$drop( +void cxxbridge05$unique_ptr$std$string$drop( std::unique_ptr *ptr) noexcept { ptr->~unique_ptr(); } } // extern "C" #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \ - size_t cxxbridge04$std$vector$##RUST_TYPE##$size( \ + size_t cxxbridge05$std$vector$##RUST_TYPE##$size( \ const std::vector &s) noexcept { \ return s.size(); \ } \ - const CXX_TYPE *cxxbridge04$std$vector$##RUST_TYPE##$get_unchecked( \ + const CXX_TYPE *cxxbridge05$std$vector$##RUST_TYPE##$get_unchecked( \ const std::vector &s, size_t pos) noexcept { \ return &s[pos]; \ } \ - void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$null( \ + void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ } \ - void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$raw( \ + void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$raw( \ std::unique_ptr> *ptr, \ std::vector *raw) noexcept { \ new (ptr) std::unique_ptr>(raw); \ } \ const std::vector \ - *cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$get( \ + *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$get( \ const std::unique_ptr> &ptr) noexcept { \ return ptr.get(); \ } \ std::vector \ - *cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$release( \ + *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$release( \ std::unique_ptr> &ptr) noexcept { \ return ptr.release(); \ } \ - void cxxbridge04$unique_ptr$std$vector$##RUST_TYPE##$drop( \ + void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$drop( \ std::unique_ptr> *ptr) noexcept { \ ptr->~unique_ptr(); \ } #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ - void cxxbridge04$rust_vec$##RUST_TYPE##$new( \ + void cxxbridge05$rust_vec$##RUST_TYPE##$new( \ rust::Vec *ptr) noexcept; \ - void cxxbridge04$rust_vec$##RUST_TYPE##$drop( \ + void cxxbridge05$rust_vec$##RUST_TYPE##$drop( \ rust::Vec *ptr) noexcept; \ - size_t cxxbridge04$rust_vec$##RUST_TYPE##$len( \ + size_t cxxbridge05$rust_vec$##RUST_TYPE##$len( \ const rust::Vec *ptr) noexcept; \ - const CXX_TYPE *cxxbridge04$rust_vec$##RUST_TYPE##$data( \ + const CXX_TYPE *cxxbridge05$rust_vec$##RUST_TYPE##$data( \ const rust::Vec *ptr) noexcept; \ - size_t cxxbridge04$rust_vec$##RUST_TYPE##$stride() noexcept; + size_t cxxbridge05$rust_vec$##RUST_TYPE##$stride() noexcept; #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \ template <> \ Vec::Vec() noexcept { \ - cxxbridge04$rust_vec$##RUST_TYPE##$new(this); \ + cxxbridge05$rust_vec$##RUST_TYPE##$new(this); \ } \ template <> \ void Vec::drop() noexcept { \ - return cxxbridge04$rust_vec$##RUST_TYPE##$drop(this); \ + return cxxbridge05$rust_vec$##RUST_TYPE##$drop(this); \ } \ template <> \ size_t Vec::size() const noexcept { \ - return cxxbridge04$rust_vec$##RUST_TYPE##$len(this); \ + return cxxbridge05$rust_vec$##RUST_TYPE##$len(this); \ } \ template <> \ const CXX_TYPE *Vec::data() const noexcept { \ - return cxxbridge04$rust_vec$##RUST_TYPE##$data(this); \ + return cxxbridge05$rust_vec$##RUST_TYPE##$data(this); \ } \ template <> \ size_t Vec::stride() noexcept { \ - return cxxbridge04$rust_vec$##RUST_TYPE##$stride(); \ + return cxxbridge05$rust_vec$##RUST_TYPE##$stride(); \ } // Usize and isize are the same type as one of the below. @@ -290,7 +290,7 @@ FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) } // extern "C" namespace rust { -inline namespace cxxbridge04 { +inline namespace cxxbridge05 { FOR_EACH_RUST_VEC(RUST_VEC_OPS) -} // namespace cxxbridge04 +} // namespace cxxbridge05 } // namespace rust diff --git a/src/cxx_string.rs b/src/cxx_string.rs index 2c712f1..7b47feb 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -5,9 +5,9 @@ use core::slice; use core::str::{self, Utf8Error}; extern "C" { - #[link_name = "cxxbridge04$cxx_string$data"] + #[link_name = "cxxbridge05$cxx_string$data"] fn string_data(_: &CxxString) -> *const u8; - #[link_name = "cxxbridge04$cxx_string$length"] + #[link_name = "cxxbridge05$cxx_string$length"] fn string_length(_: &CxxString) -> usize; } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index a5fe536..5fb0807 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -157,7 +157,7 @@ macro_rules! impl_vector_element { fn __vector_size(v: &CxxVector<$ty>) -> usize { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$size")] + #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$size")] fn __vector_size(_: &CxxVector<$ty>) -> usize; } } @@ -166,7 +166,7 @@ macro_rules! impl_vector_element { unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$std$vector$", $segment, "$get_unchecked")] + #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$get_unchecked")] fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty; } } @@ -175,7 +175,7 @@ macro_rules! impl_vector_element { fn __unique_ptr_null() -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$null")] + #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$null")] fn __unique_ptr_null(this: *mut *mut c_void); } } @@ -186,7 +186,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_raw(raw: *mut CxxVector) -> *mut c_void { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$raw")] + #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$raw")] fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); } } @@ -197,7 +197,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$get")] + #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$get")] fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; } } @@ -206,7 +206,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$release")] + #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$release")] fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; } } @@ -215,7 +215,7 @@ macro_rules! impl_vector_element { unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { extern "C" { attr! { - #[link_name = concat!("cxxbridge04$unique_ptr$std$vector$", $segment, "$drop")] + #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$drop")] fn __unique_ptr_drop(this: *mut *mut c_void); } } diff --git a/src/result.rs b/src/result.rs index 296d4a2..fcced76 100644 --- a/src/result.rs +++ b/src/result.rs @@ -34,7 +34,7 @@ unsafe fn to_c_error(msg: String) -> Result { let len = msg.len(); extern "C" { - #[link_name = "cxxbridge04$error"] + #[link_name = "cxxbridge05$error"] fn error(ptr: *const u8, len: usize) -> *const u8; } diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index da1c3f9..b408f25 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -2,7 +2,7 @@ use alloc::boxed::Box; use alloc::string::String; use core::slice; -#[export_name = "cxxbridge04$exception"] +#[export_name = "cxxbridge05$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { let slice = slice::from_raw_parts(ptr, len); let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index 823173a..b655381 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -1,7 +1,7 @@ use core::slice; use core::str; -#[export_name = "cxxbridge04$str$valid"] +#[export_name = "cxxbridge05$str$valid"] unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool { let slice = slice::from_raw_parts(ptr, len); str::from_utf8(slice).is_ok() diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 774b824..e5ab9ea 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -5,17 +5,17 @@ use core::ptr; use core::slice; use core::str; -#[export_name = "cxxbridge04$string$new"] +#[export_name = "cxxbridge05$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit) { ptr::write(this.as_mut_ptr(), String::new()); } -#[export_name = "cxxbridge04$string$clone"] +#[export_name = "cxxbridge05$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit, other: &String) { ptr::write(this.as_mut_ptr(), other.clone()); } -#[export_name = "cxxbridge04$string$from"] +#[export_name = "cxxbridge05$string$from"] unsafe extern "C" fn string_from( this: &mut MaybeUninit, ptr: *const u8, @@ -31,17 +31,17 @@ unsafe extern "C" fn string_from( } } -#[export_name = "cxxbridge04$string$drop"] +#[export_name = "cxxbridge05$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop) { ManuallyDrop::drop(this); } -#[export_name = "cxxbridge04$string$ptr"] +#[export_name = "cxxbridge05$string$ptr"] unsafe extern "C" fn string_ptr(this: &String) -> *const u8 { this.as_ptr() } -#[export_name = "cxxbridge04$string$len"] +#[export_name = "cxxbridge05$string$len"] unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index 2304abf..00a7f16 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -11,31 +11,31 @@ macro_rules! rust_vec_shims { const _: () = { attr! { - #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$new")] + #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { ptr::write(this, RustVec { repr: Vec::new() }); } } attr! { - #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$drop")] + #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { ptr::drop_in_place(this); } } attr! { - #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$len")] + #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { (*this).repr.len() } } attr! { - #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$data")] + #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { (*this).repr.as_ptr() } } attr! { - #[export_name = concat!("cxxbridge04$rust_vec$", $segment, "$stride")] + #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$stride")] unsafe extern "C" fn __stride() -> usize { mem::size_of::<$ty>() } diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 7408d40..477c716 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -174,15 +174,15 @@ pub unsafe trait UniquePtrTarget { } extern "C" { - #[link_name = "cxxbridge04$unique_ptr$std$string$null"] + #[link_name = "cxxbridge05$unique_ptr$std$string$null"] fn unique_ptr_std_string_null(this: *mut *mut c_void); - #[link_name = "cxxbridge04$unique_ptr$std$string$raw"] + #[link_name = "cxxbridge05$unique_ptr$std$string$raw"] fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); - #[link_name = "cxxbridge04$unique_ptr$std$string$get"] + #[link_name = "cxxbridge05$unique_ptr$std$string$get"] fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; - #[link_name = "cxxbridge04$unique_ptr$std$string$release"] + #[link_name = "cxxbridge05$unique_ptr$std$string$release"] fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; - #[link_name = "cxxbridge04$unique_ptr$std$string$drop"] + #[link_name = "cxxbridge05$unique_ptr$std$string$drop"] fn unique_ptr_std_string_drop(this: *mut *mut c_void); } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 1c8c917..72233b3 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -3,7 +3,7 @@ use crate::syntax::symbol::{self, Symbol}; use crate::syntax::ExternFn; use proc_macro2::Ident; -const CXXBRIDGE: &str = "cxxbridge04"; +const CXXBRIDGE: &str = "cxxbridge05"; macro_rules! join { ($($segment:expr),*) => { diff --git a/syntax/symbol.rs b/syntax/symbol.rs index c8500b9..1e5b513 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -4,7 +4,7 @@ use quote::ToTokens; use std::fmt::{self, Display, Write}; // A mangled symbol consisting of segments separated by '$'. -// For example: cxxbridge04$string$new +// For example: cxxbridge05$string$new pub struct Symbol(String); impl Display for Symbol { diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs index 139950e..527d709 100644 --- a/tests/cxx_gen.rs +++ b/tests/cxx_gen.rs @@ -18,7 +18,7 @@ fn test_extern_c_function() { let output = str::from_utf8(&generated.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. - assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); } #[test] @@ -28,5 +28,5 @@ fn test_impl_annotation() { let source = BRIDGE0.parse().unwrap(); let generated = generate_header_and_cc(source, &opt).unwrap(); let output = str::from_utf8(&generated.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); } From c499950924e05f2534ddd90fdd53fc87d0d3cecf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 02:34:50 +0000 Subject: [PATCH 1011/2232] Release 0.5.0 --- diff --git a/Cargo.toml b/Cargo.toml index 54c968f..6ee9913 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.4.7" # remember to update html_root_url +version = "0.5.0" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge05" @@ -20,16 +20,16 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.4.7", path = "macro" } +cxxbridge-macro = { version = "=0.5.0", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.4.7", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.5.0", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.4.7", path = "gen/build" } -cxx-gen = { version = "0.4", path = "gen/lib" } +cxx-build = { version = "=0.5.0", path = "gen/build" } +cxx-gen = { version = "0.5", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" trybuild = { version = "1.0.33", features = ["diff"] } diff --git a/README.md b/README.md index 38fe682..46389a3 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ can be 100% safe. ```toml [dependencies] -cxx = "0.4" +cxx = "0.5" [build-dependencies] -cxx-build = "0.4" +cxx-build = "0.5" ``` *Compiler support: requires rustc 1.42+ and c++11 or newer*
@@ -222,7 +222,7 @@ set up any additional source files and compiler flags as normal. # Cargo.toml [build-dependencies] -cxx-build = "0.4" +cxx-build = "0.5" ``` ```rust @@ -310,11 +310,11 @@ returns of functions. Stringrust::String &strrust::Str &[u8]rust::Slice<uint8_t>arbitrary &[T] not implemented yet -CxxStringstd::stringcannot be passed by value +CxxStringstd::stringcannot be passed by value Box<T>rust::Box<T>cannot hold opaque C++ type -UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type +UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type Vec<T>rust::Vec<T>cannot hold opaque C++ type -CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type +CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far Result<T>throw/catchallowed as return type only diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 5b4f666..bfaec83 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.4.7" +version = "0.5.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index bf87f1f..c6ef927 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.4.7" +version = "0.5.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -20,7 +20,7 @@ scratch = "1.0" syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [dev-dependencies] -cxx-gen = { version = "0.4", path = "../lib" } +cxx-gen = { version = "0.5", path = "../lib" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 684642c..b8354f9 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.4.7" +version = "0.5.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index faa82c2..ee4052f 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.4.1" +version = "0.5.0" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 9c151b8..9770a5e 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.4.7" +version = "0.5.0" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" @@ -19,7 +19,7 @@ quote = "1.0.4" syn = { version = "1.0.20", features = ["full"] } [dev-dependencies] -cxx = { version = "0.4", path = ".." } +cxx = { version = "0.5", path = ".." } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/lib.rs b/src/lib.rs index 9287101..cb10a18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! # Cargo.toml //! //! [build-dependencies] -//! cxx-build = "0.4" +//! cxx-build = "0.5" //! ``` //! //! ```no_run @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.4.7")] +#![doc(html_root_url = "https://docs.rs/cxx/0.5.0")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index c11e27e..6775e06 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.4.7" +version = "0.5.0" dependencies = [ "cc", "cxx-build", @@ -74,7 +74,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.4.7" +version = "0.5.0" dependencies = [ "cc", "codespan-reporting", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.4.1" +version = "0.5.0" dependencies = [ "cc", "codespan-reporting", @@ -108,7 +108,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.4.7" +version = "0.5.0" dependencies = [ "clap", "codespan-reporting", @@ -119,11 +119,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.4.7" +version = "0.5.0" [[package]] name = "cxxbridge-macro" -version = "0.4.7" +version = "0.5.0" dependencies = [ "cxx", "proc-macro2", From 5a4d53a1059cfa6c4ee189e52859b80a01f5b03e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 03:36:58 +0000 Subject: [PATCH 1012/2232] Update bazel build to rustc 1.47 --- diff --git a/WORKSPACE b/WORKSPACE index 5e0cf9e..d62b5dd 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,13 +24,13 @@ bazel_version(name = "bazel_version") load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set") rust_repository_set( - name = "rust_1_46_linux", + name = "rust_1_47_linux", exec_triple = "x86_64-unknown-linux-gnu", - version = "1.46.0", + version = "1.47.0", ) rust_repository_set( - name = "rust_1_46_darwin", + name = "rust_1_47_darwin", exec_triple = "x86_64-apple-darwin", - version = "1.46.0", + version = "1.47.0", ) From d9e789e8cf5bb0001b547e0c6f533a6febef990b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 04:22:45 +0000 Subject: [PATCH 1013/2232] Fix readme's reference to directory of cmd This moved from cmd to gen/cmd in f8ed07327b5217c3e63b44597dc026f95156bdbd. --- diff --git a/README.md b/README.md index 46389a3..7171724 100644 --- a/README.md +++ b/README.md @@ -247,7 +247,7 @@ fn main() { For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate way of invoking the C++ code generator as a standalone command line tool. The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the -*cmd* directory of this repo. +*gen/cmd* directory of this repo. ```bash $ cargo install cxxbridge-cmd diff --git a/src/lib.rs b/src/lib.rs index cb10a18..aaf4196 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -253,7 +253,7 @@ //! For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate //! way of invoking the C++ code generator as a standalone command line tool. //! The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be -//! built from the *cmd* directory of [https://github.com/dtolnay/cxx]. +//! built from the *gen/cmd* directory of [https://github.com/dtolnay/cxx]. //! //! ```bash //! $ cargo install cxxbridge-cmd From f3a9afae5015971cc8b3e857c6dadbb9142bd3f8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 09 2020 06:22:15 +0000 Subject: [PATCH 1014/2232] Omit empty derive attribute Previously an empty #[derive()] would appear distractingly in macro-expanded code. --- diff --git a/macro/src/derive.rs b/macro/src/derive.rs new file mode 100644 index 0000000..1abc5da --- /dev/null +++ b/macro/src/derive.rs @@ -0,0 +1,14 @@ +use crate::syntax::Derive; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; + +pub struct DeriveAttribute<'a>(pub &'a [Derive]); + +impl<'a> ToTokens for DeriveAttribute<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if !self.0.is_empty() { + let derives = self.0; + tokens.extend(quote!(#[derive(#(#derives),*)])); + } + } +} diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 20f166f..cb3babd 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,3 +1,4 @@ +use crate::derive::DeriveAttribute; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; @@ -128,7 +129,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; - let derives = &strct.derives; + let derives = DeriveAttribute(&strct.derives); let type_id = type_id(namespace, ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors @@ -139,7 +140,7 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { quote! { #doc - #[derive(#(#derives),*)] + #derives #[repr(C)] pub struct #ident { #(#fields,)* diff --git a/macro/src/lib.rs b/macro/src/lib.rs index fae874a..291b03c 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -9,6 +9,7 @@ extern crate proc_macro; +mod derive; mod expand; mod syntax; mod type_id; From a4641c738ac9201880a3fce3449b4f481c3d7d3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 02:12:17 +0000 Subject: [PATCH 1015/2232] Store independent rust name and c++ name for extern functions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9e6a297..5d47c63 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -370,7 +370,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.to_string(); + let local_name = method.ident.cxx.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -517,8 +517,8 @@ fn write_cxx_function_shim( write!(out, " "); write_return_type(out, &efn.ret); match &efn.receiver { - None => write!(out, "(*{}$)(", efn.ident), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), + None => write!(out, "(*{}$)(", efn.ident.rust), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -534,8 +534,8 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), + None => write!(out, "{}", efn.ident.cxx), + Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), } writeln!(out, ";"); write!(out, " "); @@ -562,8 +562,8 @@ fn write_cxx_function_shim( _ => {} } match &efn.receiver { - None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self.*{}$)(", efn.ident), + None => write!(out, "{}$(", efn.ident.rust), + Some(_) => write!(out, "(self.*{}$)(", efn.ident.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -697,8 +697,8 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), + None => efn.ident.cxx.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), }; let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index cb3babd..6fa7ea7 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -206,7 +206,6 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -238,7 +237,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types outparam = Some(quote!(__return: *mut #ret)); } let link_name = mangle::extern_fn(namespace, efn); - let local_name = format_ident!("__{}", ident); + let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] fn #local_name(#(#all_args,)* #outparam) #ret; @@ -246,7 +245,6 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); let receiver = efn.receiver.iter().map(|receiver| { @@ -329,7 +327,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } }) .collect::(); - let local_name = format_ident!("__{}", ident); + let local_name = format_ident!("__{}", efn.ident.rust); let call = if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); setup.extend(quote! { @@ -426,6 +424,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types if unsafety.is_none() { dispatch = quote!(unsafe { #dispatch }); } + let ident = &efn.ident.rust; let function_shim = quote! { #doc pub #unsafety fn #ident(#(#all_args,)*) #ret { @@ -455,7 +454,7 @@ fn expand_function_pointer_trampoline( let c_trampoline = mangle::c_trampoline(namespace, efn, var); let r_trampoline = mangle::r_trampoline(namespace, efn, var); let local_name = parse_quote!(__); - let catch_unwind_label = format!("::{}::{}", efn.ident, var); + let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( sig, types, @@ -510,11 +509,10 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let link_name = mangle::extern_fn(namespace, efn); - let local_name = format_ident!("__{}", ident); - let catch_unwind_label = format!("::{}", ident); - let invoke = Some(ident); + let local_name = format_ident!("__{}", efn.ident.rust); + let catch_unwind_label = format!("::{}", efn.ident.rust); + let invoke = Some(&efn.ident.rust); expand_rust_function_shim_impl( efn, types, diff --git a/syntax/check.rs b/syntax/check.rs index ff23ec9..2f3c334 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -214,8 +214,8 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { let what = match reason { TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), - TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident), - TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident), + TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), + TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; let msg = format!( "needs a cxx::ExternType impl in order to be used as {}", diff --git a/syntax/ident.rs b/syntax/ident.rs index 74e7799..66f7365 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -37,7 +37,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - check(cx, &efn.ident); + check(cx, &efn.ident.rust); for arg in &efn.args { check(cx, &arg.ident); } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 72233b3..e461887 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -13,8 +13,8 @@ macro_rules! join { pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident), - None => join!(namespace, CXXBRIDGE, efn.ident), + Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), + None => join!(namespace, CXXBRIDGE, efn.ident.rust), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 934f6c6..c8dea67 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -71,10 +71,15 @@ pub struct Enum { pub repr: Atom, } +pub struct Pair { + pub cxx: Ident, + pub rust: Ident, +} + pub struct ExternFn { pub lang: Lang, pub doc: Doc, - pub ident: Ident, + pub ident: Pair, pub sig: Signature, pub semi_token: Token![;], } diff --git a/syntax/parse.rs b/syntax/parse.rs index c611c8b..142d063 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,7 +3,7 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; @@ -370,7 +370,10 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R Ok(api_function(ExternFn { lang, doc, - ident, + ident: Pair { + cxx: ident.clone(), + rust: ident, + }, sig: Signature { unsafety, fn_token, diff --git a/syntax/types.rs b/syntax/types.rs index 3f8d10c..5ec7d26 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -51,7 +51,8 @@ impl<'a> Types<'a> { } let mut type_names = UnorderedSet::new(); - let mut function_names = UnorderedSet::new(); + let mut cxx_function_names = UnorderedSet::new(); + let mut rust_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. @@ -116,9 +117,15 @@ impl<'a> Types<'a> { rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - let ident = &efn.ident; - if !function_names.insert((&efn.receiver, ident)) { - duplicate_name(cx, efn, ident); + let cxx_fn = (&efn.receiver, &efn.ident.cxx); + let rust_fn = (&efn.receiver, &efn.ident.rust); + let cxx_duplicate = !cxx_function_names.insert(cxx_fn); + if !rust_function_names.insert(rust_fn) { + duplicate_name(cx, efn, &efn.ident.rust); + } else if cxx_duplicate { + // Insert into cxx_function_names either way, but hide + // error if we're already erroring on the rust name. + duplicate_name(cx, efn, &efn.ident.cxx); } for arg in &efn.args { visit(&mut all, &arg.ty); From 1d673d82a75c4960ed708850ddaa043b840bba11 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 02:44:02 +0000 Subject: [PATCH 1016/2232] Permit duplicate C++ names of extern functions C++ has overloading so that's fine. --- diff --git a/syntax/types.rs b/syntax/types.rs index 5ec7d26..5bac76e 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -51,8 +51,7 @@ impl<'a> Types<'a> { } let mut type_names = UnorderedSet::new(); - let mut cxx_function_names = UnorderedSet::new(); - let mut rust_function_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. @@ -117,15 +116,10 @@ impl<'a> Types<'a> { rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - let cxx_fn = (&efn.receiver, &efn.ident.cxx); - let rust_fn = (&efn.receiver, &efn.ident.rust); - let cxx_duplicate = !cxx_function_names.insert(cxx_fn); - if !rust_function_names.insert(rust_fn) { + // Note: duplication of the C++ name is fine because C++ has + // function overloading. + if !function_names.insert((&efn.receiver, &efn.ident.rust)) { duplicate_name(cx, efn, &efn.ident.rust); - } else if cxx_duplicate { - // Insert into cxx_function_names either way, but hide - // error if we're already erroring on the rust name. - duplicate_name(cx, efn, &efn.ident.cxx); } for arg in &efn.args { visit(&mut all, &arg.ty); From e107c1b81d55e65030f41f741d547ce20212b51e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:22:01 +0000 Subject: [PATCH 1017/2232] Merge pull request #348 from dtolnay/alias Store independent rust name and c++ name for extern functions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9e6a297..5d47c63 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -370,7 +370,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.to_string(); + let local_name = method.ident.cxx.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -517,8 +517,8 @@ fn write_cxx_function_shim( write!(out, " "); write_return_type(out, &efn.ret); match &efn.receiver { - None => write!(out, "(*{}$)(", efn.ident), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident), + None => write!(out, "(*{}$)(", efn.ident.rust), + Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -534,8 +534,8 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident), + None => write!(out, "{}", efn.ident.cxx), + Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), } writeln!(out, ";"); write!(out, " "); @@ -562,8 +562,8 @@ fn write_cxx_function_shim( _ => {} } match &efn.receiver { - None => write!(out, "{}$(", efn.ident), - Some(_) => write!(out, "(self.*{}$)(", efn.ident), + None => write!(out, "{}$(", efn.ident.rust), + Some(_) => write!(out, "(self.*{}$)(", efn.ident.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -697,8 +697,8 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident), + None => efn.ident.cxx.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), }; let invoke = mangle::extern_fn(&out.namespace, efn); let indirect_call = false; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index cb3babd..6fa7ea7 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -206,7 +206,6 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -238,7 +237,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types outparam = Some(quote!(__return: *mut #ret)); } let link_name = mangle::extern_fn(namespace, efn); - let local_name = format_ident!("__{}", ident); + let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] fn #local_name(#(#all_args,)* #outparam) #ret; @@ -246,7 +245,6 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let doc = &efn.doc; let decl = expand_cxx_function_decl(namespace, efn, types); let receiver = efn.receiver.iter().map(|receiver| { @@ -329,7 +327,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } }) .collect::(); - let local_name = format_ident!("__{}", ident); + let local_name = format_ident!("__{}", efn.ident.rust); let call = if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); setup.extend(quote! { @@ -426,6 +424,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types if unsafety.is_none() { dispatch = quote!(unsafe { #dispatch }); } + let ident = &efn.ident.rust; let function_shim = quote! { #doc pub #unsafety fn #ident(#(#all_args,)*) #ret { @@ -455,7 +454,7 @@ fn expand_function_pointer_trampoline( let c_trampoline = mangle::c_trampoline(namespace, efn, var); let r_trampoline = mangle::r_trampoline(namespace, efn, var); let local_name = parse_quote!(__); - let catch_unwind_label = format!("::{}::{}", efn.ident, var); + let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( sig, types, @@ -510,11 +509,10 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let ident = &efn.ident; let link_name = mangle::extern_fn(namespace, efn); - let local_name = format_ident!("__{}", ident); - let catch_unwind_label = format!("::{}", ident); - let invoke = Some(ident); + let local_name = format_ident!("__{}", efn.ident.rust); + let catch_unwind_label = format!("::{}", efn.ident.rust); + let invoke = Some(&efn.ident.rust); expand_rust_function_shim_impl( efn, types, diff --git a/syntax/check.rs b/syntax/check.rs index ff23ec9..2f3c334 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -214,8 +214,8 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { let what = match reason { TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), - TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident), - TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident), + TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), + TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; let msg = format!( "needs a cxx::ExternType impl in order to be used as {}", diff --git a/syntax/ident.rs b/syntax/ident.rs index 74e7799..66f7365 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -37,7 +37,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - check(cx, &efn.ident); + check(cx, &efn.ident.rust); for arg in &efn.args { check(cx, &arg.ident); } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 72233b3..e461887 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -13,8 +13,8 @@ macro_rules! join { pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident), - None => join!(namespace, CXXBRIDGE, efn.ident), + Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), + None => join!(namespace, CXXBRIDGE, efn.ident.rust), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 934f6c6..c8dea67 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -71,10 +71,15 @@ pub struct Enum { pub repr: Atom, } +pub struct Pair { + pub cxx: Ident, + pub rust: Ident, +} + pub struct ExternFn { pub lang: Lang, pub doc: Doc, - pub ident: Ident, + pub ident: Pair, pub sig: Signature, pub semi_token: Token![;], } diff --git a/syntax/parse.rs b/syntax/parse.rs index c611c8b..142d063 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,7 +3,7 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; @@ -370,7 +370,10 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R Ok(api_function(ExternFn { lang, doc, - ident, + ident: Pair { + cxx: ident.clone(), + rust: ident, + }, sig: Signature { unsafety, fn_token, diff --git a/syntax/types.rs b/syntax/types.rs index 3f8d10c..5bac76e 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -116,9 +116,10 @@ impl<'a> Types<'a> { rust.insert(ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - let ident = &efn.ident; - if !function_names.insert((&efn.receiver, ident)) { - duplicate_name(cx, efn, ident); + // Note: duplication of the C++ name is fine because C++ has + // function overloading. + if !function_names.insert((&efn.receiver, &efn.ident.rust)) { + duplicate_name(cx, efn, &efn.ident.rust); } for arg in &efn.args { visit(&mut all, &arg.ty); From 1039a24ee4fed457873d8e07b8821db34ca1347f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:22:22 +0000 Subject: [PATCH 1018/2232] Attribute parser for cxx_name and rust_name attributes --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4c76641..4c8a3e5 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -10,6 +10,8 @@ pub struct Parser<'a> { pub doc: Option<&'a mut Doc>, pub derives: Option<&'a mut Vec>, pub repr: Option<&'a mut Option>, + pub cxx_name: Option<&'a mut Option>, + pub rust_name: Option<&'a mut Option>, } pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { @@ -57,6 +59,26 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } else if attr.path.is_ident("cxx_name") { + match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + Ok(attr) => { + if let Some(cxx_name) = &mut parser.cxx_name { + **cxx_name = Some(attr); + continue; + } + } + Err(err) => return cx.push(err), + } + } else if attr.path.is_ident("rust_name") { + match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + Ok(attr) => { + if let Some(rust_name) = &mut parser.rust_name { + **rust_name = Some(attr); + continue; + } + } + Err(err) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -99,3 +121,13 @@ fn parse_repr_attribute(input: ParseStream) -> Result { "unrecognized repr", )) } + +fn parse_function_alias_attribute(input: ParseStream) -> Result { + input.parse::()?; + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + lit.parse() + } else { + input.parse() + } +} From 938ca8585645875dd8ab04a1b1e2e691f936a86c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:22:23 +0000 Subject: [PATCH 1019/2232] Translate cxx_/rust_name from attribute to syntax tree --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 142d063..367ad05 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,6 +300,20 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R )); } + let mut doc = Doc::new(); + let mut cxx_name = None; + let mut rust_name = None; + attrs::parse( + cx, + &foreign_fn.attrs, + attrs::Parser { + doc: Some(&mut doc), + cxx_name: Some(&mut cxx_name), + rust_name: Some(&mut rust_name), + ..Default::default() + }, + ); + let mut receiver = None; let mut args = Punctuated::new(); for arg in foreign_fn.sig.inputs.pairs() { @@ -356,10 +370,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R let mut throws_tokens = None; let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); - let doc = attrs::parse_doc(cx, &foreign_fn.attrs); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; - let ident = foreign_fn.sig.ident.clone(); + let ident = Pair { + cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), + }; let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; let api_function = match lang { @@ -370,10 +386,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R Ok(api_function(ExternFn { lang, doc, - ident: Pair { - cxx: ident.clone(), - rust: ident, - }, + ident, sig: Signature { unsafety, fn_token, From 3bbcdbbbf62c846e2920daa615dbab0a9bc888db Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:22:23 +0000 Subject: [PATCH 1020/2232] Add test of cxx_/rust_name attributes --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index cc07752..5cfd97b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -101,6 +101,15 @@ pub mod ffi { fn set2(&mut self, n: usize) -> usize; fn set_succeed(&mut self, n: usize) -> Result; fn get_fail(&mut self) -> Result; + + #[rust_name = "i32_overloaded_method"] + fn cOverloadedMethod(&self, x: i32) -> String; + #[rust_name = "str_overloaded_method"] + fn cOverloadedMethod(&self, x: &str) -> String; + #[rust_name = "i32_overloaded_function"] + fn cOverloadedFunction(x: i32) -> String; + #[rust_name = "str_overloaded_function"] + fn cOverloadedFunction(x: &str) -> String; } extern "C" { @@ -160,6 +169,9 @@ pub mod ffi { fn r_return_r2(n: usize) -> Box; fn get(self: &R2) -> usize; fn set(self: &mut R2, n: usize) -> usize; + + #[cxx_name = "rAliasedFunction"] + fn r_aliased_function(x: i32) -> String; } } @@ -358,3 +370,7 @@ fn r_fail_return_primitive() -> Result { fn r_return_r2(n: usize) -> Box { Box::new(R2(n)) } + +fn r_aliased_function(x: i32) -> String { + x.to_string() +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 9cb6ed0..3a1a03f 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -365,6 +365,22 @@ extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept { return std::unique_ptr(new std::string("2020")).release(); } +rust::String C::cOverloadedMethod(int32_t x) const { + return rust::String(std::to_string(x)); +} + +rust::String C::cOverloadedMethod(rust::Str x) const { + return rust::String(std::string(x)); +} + +rust::String cOverloadedFunction(int x) { + return rust::String(std::to_string(x)); +} + +rust::String cOverloadedFunction(rust::Str x) { + return rust::String(std::string(x)); +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -421,6 +437,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r2->set(2020) == 2020); ASSERT(r2->get() == 2020); + ASSERT(std::string(rAliasedFunction(2020)) == "2020"); + cxx_test_suite_set_correct(); return nullptr; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f3bc2dd..9affe7d 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -20,6 +20,8 @@ public: size_t get_fail(); const std::vector &get_v() const; std::vector &get_v(); + rust::String cOverloadedMethod(int32_t x) const; + rust::String cOverloadedMethod(rust::Str x) const; private: size_t n; @@ -101,4 +103,7 @@ rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +rust::String cOverloadedFunction(int32_t x); +rust::String cOverloadedFunction(rust::Str x); + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index 9c71bd5..ea29e62 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -181,3 +181,12 @@ extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool { *r == 2020 } + +#[test] +fn test_rust_name_attribute() { + assert_eq!("2020", ffi::i32_overloaded_function(2020)); + assert_eq!("2020", ffi::str_overloaded_function("2020")); + let unique_ptr = ffi::c_return_unique_ptr(); + assert_eq!("2020", unique_ptr.i32_overloaded_method(2020)); + assert_eq!("2020", unique_ptr.str_overloaded_method("2020")); +} From df4b677abf6f3711e69f7b7027e7177a2627609f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:30:30 +0000 Subject: [PATCH 1021/2232] Merge pull request #349 from dtolnay/alias Support functions having distinct Rust name and C++ name --- diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4c76641..4c8a3e5 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -10,6 +10,8 @@ pub struct Parser<'a> { pub doc: Option<&'a mut Doc>, pub derives: Option<&'a mut Vec>, pub repr: Option<&'a mut Option>, + pub cxx_name: Option<&'a mut Option>, + pub rust_name: Option<&'a mut Option>, } pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { @@ -57,6 +59,26 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } else if attr.path.is_ident("cxx_name") { + match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + Ok(attr) => { + if let Some(cxx_name) = &mut parser.cxx_name { + **cxx_name = Some(attr); + continue; + } + } + Err(err) => return cx.push(err), + } + } else if attr.path.is_ident("rust_name") { + match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + Ok(attr) => { + if let Some(rust_name) = &mut parser.rust_name { + **rust_name = Some(attr); + continue; + } + } + Err(err) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -99,3 +121,13 @@ fn parse_repr_attribute(input: ParseStream) -> Result { "unrecognized repr", )) } + +fn parse_function_alias_attribute(input: ParseStream) -> Result { + input.parse::()?; + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + lit.parse() + } else { + input.parse() + } +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 142d063..367ad05 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -300,6 +300,20 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R )); } + let mut doc = Doc::new(); + let mut cxx_name = None; + let mut rust_name = None; + attrs::parse( + cx, + &foreign_fn.attrs, + attrs::Parser { + doc: Some(&mut doc), + cxx_name: Some(&mut cxx_name), + rust_name: Some(&mut rust_name), + ..Default::default() + }, + ); + let mut receiver = None; let mut args = Punctuated::new(); for arg in foreign_fn.sig.inputs.pairs() { @@ -356,10 +370,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R let mut throws_tokens = None; let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; let throws = throws_tokens.is_some(); - let doc = attrs::parse_doc(cx, &foreign_fn.attrs); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; - let ident = foreign_fn.sig.ident.clone(); + let ident = Pair { + cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), + }; let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; let api_function = match lang { @@ -370,10 +386,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R Ok(api_function(ExternFn { lang, doc, - ident: Pair { - cxx: ident.clone(), - rust: ident, - }, + ident, sig: Signature { unsafety, fn_token, diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index cc07752..5cfd97b 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -101,6 +101,15 @@ pub mod ffi { fn set2(&mut self, n: usize) -> usize; fn set_succeed(&mut self, n: usize) -> Result; fn get_fail(&mut self) -> Result; + + #[rust_name = "i32_overloaded_method"] + fn cOverloadedMethod(&self, x: i32) -> String; + #[rust_name = "str_overloaded_method"] + fn cOverloadedMethod(&self, x: &str) -> String; + #[rust_name = "i32_overloaded_function"] + fn cOverloadedFunction(x: i32) -> String; + #[rust_name = "str_overloaded_function"] + fn cOverloadedFunction(x: &str) -> String; } extern "C" { @@ -160,6 +169,9 @@ pub mod ffi { fn r_return_r2(n: usize) -> Box; fn get(self: &R2) -> usize; fn set(self: &mut R2, n: usize) -> usize; + + #[cxx_name = "rAliasedFunction"] + fn r_aliased_function(x: i32) -> String; } } @@ -358,3 +370,7 @@ fn r_fail_return_primitive() -> Result { fn r_return_r2(n: usize) -> Box { Box::new(R2(n)) } + +fn r_aliased_function(x: i32) -> String { + x.to_string() +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 9cb6ed0..3a1a03f 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -365,6 +365,22 @@ extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept { return std::unique_ptr(new std::string("2020")).release(); } +rust::String C::cOverloadedMethod(int32_t x) const { + return rust::String(std::to_string(x)); +} + +rust::String C::cOverloadedMethod(rust::Str x) const { + return rust::String(std::string(x)); +} + +rust::String cOverloadedFunction(int x) { + return rust::String(std::to_string(x)); +} + +rust::String cOverloadedFunction(rust::Str x) { + return rust::String(std::string(x)); +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -421,6 +437,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r2->set(2020) == 2020); ASSERT(r2->get() == 2020); + ASSERT(std::string(rAliasedFunction(2020)) == "2020"); + cxx_test_suite_set_correct(); return nullptr; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f3bc2dd..9affe7d 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -20,6 +20,8 @@ public: size_t get_fail(); const std::vector &get_v() const; std::vector &get_v(); + rust::String cOverloadedMethod(int32_t x) const; + rust::String cOverloadedMethod(rust::Str x) const; private: size_t n; @@ -101,4 +103,7 @@ rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +rust::String cOverloadedFunction(int32_t x); +rust::String cOverloadedFunction(rust::Str x); + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index 9c71bd5..ea29e62 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -181,3 +181,12 @@ extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R { unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool { *r == 2020 } + +#[test] +fn test_rust_name_attribute() { + assert_eq!("2020", ffi::i32_overloaded_function(2020)); + assert_eq!("2020", ffi::str_overloaded_function("2020")); + let unique_ptr = ffi::c_return_unique_ptr(); + assert_eq!("2020", unique_ptr.i32_overloaded_method(2020)); + assert_eq!("2020", unique_ptr.str_overloaded_method("2020")); +} From 9e43a055b69cbdad396b634e057af5c6639c79cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:30:52 +0000 Subject: [PATCH 1022/2232] Run clippy in CI --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec4224..aa8d4a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,3 +96,11 @@ jobs: cargo vendor --versioned-dirs --locked third-party/vendor - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@clippy + - run: cargo clippy --workspace -- -Dclippy::all From 753b8f743af90feeb66a72ae8be6f31be290e5a3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:30:52 +0000 Subject: [PATCH 1023/2232] Suppress some currently triggering clippy lints --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 7148d7b..128d933 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -46,6 +46,7 @@ //! ``` #![allow( + clippy::drop_copy, clippy::inherent_to_string, clippy::needless_doctest_main, clippy::new_without_default, diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index 6456200..dae7e78 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -8,6 +8,12 @@ //! [https://github.com/google/autocxx]: https://github.com/google/autocxx #![allow(dead_code)] +#![allow( + clippy::inherent_to_string, + clippy::new_without_default, + clippy::or_fun_call, + clippy::toplevel_ref_arg +)] mod error; mod gen; From 61863c637fd265ece9a7d0dc4a2aad39a587087a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:40:11 +0000 Subject: [PATCH 1024/2232] Merge pull request #350 from dtolnay/clippy Run clippy in CI --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec4224..aa8d4a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,3 +96,11 @@ jobs: cargo vendor --versioned-dirs --locked third-party/vendor - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@clippy + - run: cargo clippy --workspace -- -Dclippy::all From c4dcb91ae6a222173fb211e575c194660933f8ea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:40:42 +0000 Subject: [PATCH 1025/2232] Automatically run cargo vendor from bazel --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa8d4a2..cf29d25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,10 +90,6 @@ jobs: chmod +x install.sh ./install.sh --user echo ::add-path::$HOME/bin - - name: Vendor dependencies - run: | - cp third-party/Cargo.lock . - cargo vendor --versioned-dirs --locked third-party/vendor - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress diff --git a/WORKSPACE b/WORKSPACE index d62b5dd..6093eee 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,4 +1,5 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//tools/bazel:vendor.bzl", "vendor") http_archive( name = "io_bazel_rules_rust", @@ -34,3 +35,8 @@ rust_repository_set( exec_triple = "x86_64-apple-darwin", version = "1.47.0", ) + +vendor( + name = "third-party", + lockfile = "//third-party:Cargo.lock", +) diff --git a/third-party/BUILD b/third-party/BUILD index 49bf512..6303eec 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -1,5 +1,6 @@ load( "//tools/bazel:rust.bzl", + glob = "third_party_glob", rust_binary = "third_party_rust_binary", rust_library = "third_party_rust_library", ) diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl index 3ef4d91..b7b23e5 100644 --- a/tools/bazel/rust.bzl +++ b/tools/bazel/rust.bzl @@ -4,6 +4,10 @@ load( _rust_library = "rust_library", _rust_test = "rust_test", ) +load("@third-party//:vendor.bzl", "vendored") + +def third_party_glob(include): + return vendored and native.glob(include) def rust_binary(edition = "2018", **kwargs): _rust_binary(edition = edition, **kwargs) diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl new file mode 100644 index 0000000..e9f10ac --- /dev/null +++ b/tools/bazel/vendor.bzl @@ -0,0 +1,54 @@ +def _impl(repository_ctx): + # Link cxx repository into @third-party. + lockfile = repository_ctx.path(repository_ctx.attr.lockfile) + workspace = lockfile.dirname.dirname + repository_ctx.symlink(workspace, "workspace") + + # Copy third-party/Cargo.lock since those are the crate versions that the + # BUILD file is written against. + vendor_lockfile = repository_ctx.path("workspace/third-party/Cargo.lock") + root_lockfile = repository_ctx.path("workspace/Cargo.lock") + _copy_file(repository_ctx, src = vendor_lockfile, dst = root_lockfile) + + # Execute cargo vendor. + cmd = ["cargo", "vendor", "--versioned-dirs", "third-party/vendor"] + result = repository_ctx.execute( + cmd, + quiet = True, + working_directory = "workspace", + ) + _log_cargo_vendor(repository_ctx, result) + if result.return_code != 0: + fail("failed to execute `{}`".format(" ".join(cmd))) + + # Copy lockfile back to third-party/Cargo.lock to reflect any modification + # performed by Cargo. + _copy_file(repository_ctx, src = root_lockfile, dst = vendor_lockfile) + + # Produce a token for third_party_glob to depend on so that the necessary + # sequencing is visible to Bazel. + repository_ctx.file("BUILD", executable = False) + repository_ctx.file("vendor.bzl", "vendored = True", executable = False) + +def _copy_file(repository_ctx, *, src, dst): + content = repository_ctx.read(src) + if not dst.exists or content != repository_ctx.read(dst): + repository_ctx.file(dst, content = content, executable = False) + +def _log_cargo_vendor(repository_ctx, result): + relevant = "" + for line in result.stderr.splitlines(True): + if line.strip() and not line.startswith("To use vendored sources,"): + relevant += line + if relevant: + # Render it as command output. + # If we just use print(), Bazel will cache and repeat the output even + # when not rerunning the command. + print = ["echo", relevant] + repository_ctx.execute(print, quiet = False) + +vendor = repository_rule( + attrs = {"lockfile": attr.label()}, + local = True, + implementation = _impl, +) From ba97787b3c33bd126731ec8b57e0bdb6286629d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 03:48:10 +0000 Subject: [PATCH 1026/2232] Merge pull request #352 from dtolnay/vendor Automatically run cargo vendor from bazel --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa8d4a2..cf29d25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,10 +90,6 @@ jobs: chmod +x install.sh ./install.sh --user echo ::add-path::$HOME/bin - - name: Vendor dependencies - run: | - cp third-party/Cargo.lock . - cargo vendor --versioned-dirs --locked third-party/vendor - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress diff --git a/WORKSPACE b/WORKSPACE index d62b5dd..6093eee 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,4 +1,5 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//tools/bazel:vendor.bzl", "vendor") http_archive( name = "io_bazel_rules_rust", @@ -34,3 +35,8 @@ rust_repository_set( exec_triple = "x86_64-apple-darwin", version = "1.47.0", ) + +vendor( + name = "third-party", + lockfile = "//third-party:Cargo.lock", +) diff --git a/third-party/BUILD b/third-party/BUILD index 49bf512..6303eec 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -1,5 +1,6 @@ load( "//tools/bazel:rust.bzl", + glob = "third_party_glob", rust_binary = "third_party_rust_binary", rust_library = "third_party_rust_library", ) diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl index 3ef4d91..b7b23e5 100644 --- a/tools/bazel/rust.bzl +++ b/tools/bazel/rust.bzl @@ -4,6 +4,10 @@ load( _rust_library = "rust_library", _rust_test = "rust_test", ) +load("@third-party//:vendor.bzl", "vendored") + +def third_party_glob(include): + return vendored and native.glob(include) def rust_binary(edition = "2018", **kwargs): _rust_binary(edition = edition, **kwargs) diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl new file mode 100644 index 0000000..e9f10ac --- /dev/null +++ b/tools/bazel/vendor.bzl @@ -0,0 +1,54 @@ +def _impl(repository_ctx): + # Link cxx repository into @third-party. + lockfile = repository_ctx.path(repository_ctx.attr.lockfile) + workspace = lockfile.dirname.dirname + repository_ctx.symlink(workspace, "workspace") + + # Copy third-party/Cargo.lock since those are the crate versions that the + # BUILD file is written against. + vendor_lockfile = repository_ctx.path("workspace/third-party/Cargo.lock") + root_lockfile = repository_ctx.path("workspace/Cargo.lock") + _copy_file(repository_ctx, src = vendor_lockfile, dst = root_lockfile) + + # Execute cargo vendor. + cmd = ["cargo", "vendor", "--versioned-dirs", "third-party/vendor"] + result = repository_ctx.execute( + cmd, + quiet = True, + working_directory = "workspace", + ) + _log_cargo_vendor(repository_ctx, result) + if result.return_code != 0: + fail("failed to execute `{}`".format(" ".join(cmd))) + + # Copy lockfile back to third-party/Cargo.lock to reflect any modification + # performed by Cargo. + _copy_file(repository_ctx, src = root_lockfile, dst = vendor_lockfile) + + # Produce a token for third_party_glob to depend on so that the necessary + # sequencing is visible to Bazel. + repository_ctx.file("BUILD", executable = False) + repository_ctx.file("vendor.bzl", "vendored = True", executable = False) + +def _copy_file(repository_ctx, *, src, dst): + content = repository_ctx.read(src) + if not dst.exists or content != repository_ctx.read(dst): + repository_ctx.file(dst, content = content, executable = False) + +def _log_cargo_vendor(repository_ctx, result): + relevant = "" + for line in result.stderr.splitlines(True): + if line.strip() and not line.startswith("To use vendored sources,"): + relevant += line + if relevant: + # Render it as command output. + # If we just use print(), Bazel will cache and repeat the output even + # when not rerunning the command. + print = ["echo", relevant] + repository_ctx.execute(print, quiet = False) + +vendor = repository_rule( + attrs = {"lockfile": attr.label()}, + local = True, + implementation = _impl, +) From 2e18e5855d6e8b6bb958ea86992cde8205aa1468 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 05:13:48 +0000 Subject: [PATCH 1027/2232] Replace ::add-path with using Environment Files https://github.com/dtolnay/cxx/issues/351 --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf29d25..916b6da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: mkdir bin wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex # dev branch from 2020.09.21 chmod +x bin/buck - echo ::add-path::bin + echo bin >> $GITHUB_PATH - name: Install lld run: sudo apt install lld - name: Vendor dependencies @@ -89,7 +89,7 @@ jobs: wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh chmod +x install.sh ./install.sh --user - echo ::add-path::$HOME/bin + echo $HOME/bin >> $GITHUB_PATH - run: bazel run demo --verbose_failures --noshow_progress - run: bazel test ... --verbose_failures --noshow_progress From 957503360bdddce99e7c078ba0ba28ca7cecc859 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 05:17:10 +0000 Subject: [PATCH 1028/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index afc696b..93abbdc 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -72,7 +72,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.42/src/**"]), + srcs = glob(["vendor/syn-1.0.43/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 6303eec..f798190 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -78,7 +78,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.42/src/**"]), + srcs = glob(["vendor/syn-1.0.43/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 6775e06..132a976 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -267,9 +267,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" +checksum = "1e2e59c50ed8f6b050b071aa7b6865293957a9af6b58b94f97c1c9434ad440ea" dependencies = [ "proc-macro2", "quote", From 52830f59d618b670b4afd3f779777c683be13cd1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 05:21:27 +0000 Subject: [PATCH 1029/2232] Release 0.5.1 --- diff --git a/Cargo.toml b/Cargo.toml index 6ee9913..045ecc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.5.0" # remember to update html_root_url +version = "0.5.1" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge05" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.5.0", path = "macro" } +cxxbridge-macro = { version = "=0.5.1", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.5.0", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.5.1", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.5.0", path = "gen/build" } +cxx-build = { version = "=0.5.1", path = "gen/build" } cxx-gen = { version = "0.5", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" diff --git a/flags/Cargo.toml b/flags/Cargo.toml index bfaec83..f8e5a7b 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.5.0" +version = "0.5.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index c6ef927..df6eea0 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.5.0" +version = "0.5.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index b8354f9..99addee 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.5.0" +version = "0.5.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index ee4052f..18845f8 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.5.0" +version = "0.5.1" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 9770a5e..1618b0d 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.5.0" +version = "0.5.1" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index aaf4196..7157c0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.5.0")] +#![doc(html_root_url = "https://docs.rs/cxx/0.5.1")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 132a976..54f250e 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.5.0" +version = "0.5.1" dependencies = [ "cc", "cxx-build", @@ -74,7 +74,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.5.0" +version = "0.5.1" dependencies = [ "cc", "codespan-reporting", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.5.0" +version = "0.5.1" dependencies = [ "cc", "codespan-reporting", @@ -108,7 +108,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.5.0" +version = "0.5.1" dependencies = [ "clap", "codespan-reporting", @@ -119,11 +119,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.5.0" +version = "0.5.1" [[package]] name = "cxxbridge-macro" -version = "0.5.0" +version = "0.5.1" dependencies = [ "cxx", "proc-macro2", From 121cca4a06788281738ff79ec376adeb5f1c8eb3 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 10 2020 23:01:26 +0000 Subject: [PATCH 1030/2232] Add tests for ExternType. --- diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index e583944..4b2cbdf 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "module.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs new file mode 100644 index 0000000..cd6ffe8 --- /dev/null +++ b/tests/ffi/extra.rs @@ -0,0 +1,35 @@ +// Separate mod so that &self in the lib.rs mod +// has an unambiguous receiver. +// At the moment, the cxx C++ codegen can't convert +// more than one cxx::bridge mod per file, so that's why +// we need to put this outside of lib.rs. +// All of this could go into module.rs instead, but +// for now its purpose is narrowly scoped for testing +// aliasing between cxx::bridge mods, so we'll keep it that +// way and start a new mod here. + +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi2 { + + impl UniquePtr {} + impl UniquePtr {} + + extern "C" { + include!("tests/ffi/tests.h"); + + type D = crate::other::D; + type E = crate::other::E; + + fn c_take_trivial_ptr(d: UniquePtr); + fn c_take_trivial_ref(d: &D); + fn c_take_trivial(d: D); + fn c_take_opaque_ptr(e: UniquePtr); + fn c_take_opaque_ref(e: &E); + fn c_return_trivial_ptr() -> UniquePtr; + fn c_return_trivial() -> D; + fn c_return_opaque_ptr() -> UniquePtr; + } +} \ No newline at end of file diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 5cfd97b..a0a2a73 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -5,10 +5,37 @@ )] pub mod module; +pub mod extra; use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; +mod other { + use cxx::kind::{Opaque, Trivial}; + use cxx::{type_id, CxxString, ExternType}; + + #[repr(C)] + pub struct D { + d: u64, + } + + #[repr(C)] + pub struct E { + e: u64, + e_str: CxxString, + } + + unsafe impl ExternType for D { + type Id = type_id!("tests::D"); + type Kind = Trivial; + } + + unsafe impl ExternType for E { + type Id = type_id!("tests::E"); + type Kind = Opaque; + } +} + #[cxx::bridge(namespace = tests)] pub mod ffi { #[derive(Clone)] diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 3a1a03f..983cf95 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -4,6 +4,8 @@ #include #include #include +#include +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -381,6 +383,54 @@ rust::String cOverloadedFunction(rust::Str x) { return rust::String(std::string(x)); } +void c_take_trivial_ptr(std::unique_ptr d) { + if (d->d == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ref(const D& d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } +} +void c_take_trivial(D d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_opaque_ptr(std::unique_ptr e) { + if (e->e == 40) { + cxx_test_suite_set_correct(); + } +} + +void c_take_opaque_ref(const E& e) { + if (e.e == 40 && e.e_str == "hello") { + cxx_test_suite_set_correct(); + } +} + +std::unique_ptr c_return_trivial_ptr() { + auto d = std::unique_ptr(new D()); + d->d = 30; + return d; +} + +D c_return_trivial() { + D d; + d.d = 30; + return d; +} + +std::unique_ptr c_return_opaque_ptr() { + auto e = std::unique_ptr(new E()); + e->e = 40; + e->e_str = std::string("hello"); + return e; +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 9affe7d..b3f547e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -28,6 +28,15 @@ private: std::vector v; }; +struct D { + uint64_t d; +}; + +struct E { + uint64_t e; + std::string e_str; +}; + enum COwnedEnum { CVal1, CVal2, @@ -103,6 +112,15 @@ rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +void c_take_trivial_ptr(std::unique_ptr d); +void c_take_trivial_ref(const D& d); +void c_take_trivial(D d); +void c_take_opaque_ptr(std::unique_ptr e); +void c_take_opaque_ref(const E& e); +std::unique_ptr c_return_trivial_ptr(); +D c_return_trivial(); +std::unique_ptr c_return_opaque_ptr(); + rust::String cOverloadedFunction(int32_t x); rust::String cOverloadedFunction(rust::Str x); diff --git a/tests/test.rs b/tests/test.rs index ea29e62..9828429 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,5 @@ use cxx_test_suite::ffi; +use cxx_test_suite::extra::ffi2; use std::cell::Cell; use std::ffi::CStr; @@ -190,3 +191,19 @@ fn test_rust_name_attribute() { assert_eq!("2020", unique_ptr.i32_overloaded_method(2020)); assert_eq!("2020", unique_ptr.str_overloaded_method("2020")); } + +#[test] +fn test_extern_trivial() { + let d = ffi2::c_return_trivial(); + check!(ffi2::c_take_trivial_ref(&d)); + check!(ffi2::c_take_trivial(d)); + let d = ffi2::c_return_trivial_ptr(); + check!(ffi2::c_take_trivial_ptr(d)); +} + +#[test] +fn test_extern_opaque() { + let e = ffi2::c_return_opaque_ptr(); + check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); + check!(ffi2::c_take_opaque_ptr(e)); +} From 1931ccf65e1ff79376cba8276dbbe88c35ddc730 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 10 2020 23:19:20 +0000 Subject: [PATCH 1031/2232] Attempt at BUCK and BUILD files. --- diff --git a/tests/BUCK b/tests/BUCK index f066542..6ad9f90 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -9,6 +9,7 @@ rust_test( rust_library( name = "ffi", srcs = [ + "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", ], @@ -25,6 +26,7 @@ cxx_library( "ffi/tests.cc", ":bridge/source", ":module/source", + ":extra/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -42,3 +44,8 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) + +rust_cxx_bridge( + name = "extra", + src = "ffi/extra.rs", +) diff --git a/tests/BUILD b/tests/BUILD index 1345a7a..4dff0a0 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -12,6 +12,7 @@ rust_test( rust_library( name = "cxx_test_suite", srcs = [ + "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", ], @@ -27,6 +28,7 @@ cc_library( "ffi/tests.cc", ":bridge/source", ":module/source", + ":extra/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -46,3 +48,9 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) + +rust_cxx_bridge( + name = "extra", + src = "ffi/extra.rs", + deps = [":impl"], +) From 2fe955f4214a651af8ed76a0df94274d839d9cee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 23:26:58 +0000 Subject: [PATCH 1032/2232] Merge pull request #356 from adetaylor/add-some-tests Add tests for ExternType. --- diff --git a/tests/BUCK b/tests/BUCK index f066542..6ad9f90 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -9,6 +9,7 @@ rust_test( rust_library( name = "ffi", srcs = [ + "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", ], @@ -25,6 +26,7 @@ cxx_library( "ffi/tests.cc", ":bridge/source", ":module/source", + ":extra/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -42,3 +44,8 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) + +rust_cxx_bridge( + name = "extra", + src = "ffi/extra.rs", +) diff --git a/tests/BUILD b/tests/BUILD index 1345a7a..4dff0a0 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -12,6 +12,7 @@ rust_test( rust_library( name = "cxx_test_suite", srcs = [ + "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", ], @@ -27,6 +28,7 @@ cc_library( "ffi/tests.cc", ":bridge/source", ":module/source", + ":extra/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -46,3 +48,9 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) + +rust_cxx_bridge( + name = "extra", + src = "ffi/extra.rs", + deps = [":impl"], +) diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index e583944..4b2cbdf 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "module.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs new file mode 100644 index 0000000..cd6ffe8 --- /dev/null +++ b/tests/ffi/extra.rs @@ -0,0 +1,35 @@ +// Separate mod so that &self in the lib.rs mod +// has an unambiguous receiver. +// At the moment, the cxx C++ codegen can't convert +// more than one cxx::bridge mod per file, so that's why +// we need to put this outside of lib.rs. +// All of this could go into module.rs instead, but +// for now its purpose is narrowly scoped for testing +// aliasing between cxx::bridge mods, so we'll keep it that +// way and start a new mod here. + +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi2 { + + impl UniquePtr {} + impl UniquePtr {} + + extern "C" { + include!("tests/ffi/tests.h"); + + type D = crate::other::D; + type E = crate::other::E; + + fn c_take_trivial_ptr(d: UniquePtr); + fn c_take_trivial_ref(d: &D); + fn c_take_trivial(d: D); + fn c_take_opaque_ptr(e: UniquePtr); + fn c_take_opaque_ref(e: &E); + fn c_return_trivial_ptr() -> UniquePtr; + fn c_return_trivial() -> D; + fn c_return_opaque_ptr() -> UniquePtr; + } +} \ No newline at end of file diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 5cfd97b..a0a2a73 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -5,10 +5,37 @@ )] pub mod module; +pub mod extra; use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; +mod other { + use cxx::kind::{Opaque, Trivial}; + use cxx::{type_id, CxxString, ExternType}; + + #[repr(C)] + pub struct D { + d: u64, + } + + #[repr(C)] + pub struct E { + e: u64, + e_str: CxxString, + } + + unsafe impl ExternType for D { + type Id = type_id!("tests::D"); + type Kind = Trivial; + } + + unsafe impl ExternType for E { + type Id = type_id!("tests::E"); + type Kind = Opaque; + } +} + #[cxx::bridge(namespace = tests)] pub mod ffi { #[derive(Clone)] diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 3a1a03f..983cf95 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -4,6 +4,8 @@ #include #include #include +#include +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -381,6 +383,54 @@ rust::String cOverloadedFunction(rust::Str x) { return rust::String(std::string(x)); } +void c_take_trivial_ptr(std::unique_ptr d) { + if (d->d == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ref(const D& d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } +} +void c_take_trivial(D d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_opaque_ptr(std::unique_ptr e) { + if (e->e == 40) { + cxx_test_suite_set_correct(); + } +} + +void c_take_opaque_ref(const E& e) { + if (e.e == 40 && e.e_str == "hello") { + cxx_test_suite_set_correct(); + } +} + +std::unique_ptr c_return_trivial_ptr() { + auto d = std::unique_ptr(new D()); + d->d = 30; + return d; +} + +D c_return_trivial() { + D d; + d.d = 30; + return d; +} + +std::unique_ptr c_return_opaque_ptr() { + auto e = std::unique_ptr(new E()); + e->e = 40; + e->e_str = std::string("hello"); + return e; +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 9affe7d..b3f547e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -28,6 +28,15 @@ private: std::vector v; }; +struct D { + uint64_t d; +}; + +struct E { + uint64_t e; + std::string e_str; +}; + enum COwnedEnum { CVal1, CVal2, @@ -103,6 +112,15 @@ rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +void c_take_trivial_ptr(std::unique_ptr d); +void c_take_trivial_ref(const D& d); +void c_take_trivial(D d); +void c_take_opaque_ptr(std::unique_ptr e); +void c_take_opaque_ref(const E& e); +std::unique_ptr c_return_trivial_ptr(); +D c_return_trivial(); +std::unique_ptr c_return_opaque_ptr(); + rust::String cOverloadedFunction(int32_t x); rust::String cOverloadedFunction(rust::Str x); diff --git a/tests/test.rs b/tests/test.rs index ea29e62..9828429 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,5 @@ use cxx_test_suite::ffi; +use cxx_test_suite::extra::ffi2; use std::cell::Cell; use std::ffi::CStr; @@ -190,3 +191,19 @@ fn test_rust_name_attribute() { assert_eq!("2020", unique_ptr.i32_overloaded_method(2020)); assert_eq!("2020", unique_ptr.str_overloaded_method("2020")); } + +#[test] +fn test_extern_trivial() { + let d = ffi2::c_return_trivial(); + check!(ffi2::c_take_trivial_ref(&d)); + check!(ffi2::c_take_trivial(d)); + let d = ffi2::c_return_trivial_ptr(); + check!(ffi2::c_take_trivial_ptr(d)); +} + +#[test] +fn test_extern_opaque() { + let e = ffi2::c_return_opaque_ptr(); + check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); + check!(ffi2::c_take_opaque_ptr(e)); +} From 362c9f92b0703cd205a55bb5b9d687df4cc3ad59 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 10 2020 23:30:04 +0000 Subject: [PATCH 1033/2232] Touch up PR 356 --- diff --git a/tests/BUCK b/tests/BUCK index 6ad9f90..0ddf1a8 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -25,8 +25,8 @@ cxx_library( srcs = [ "ffi/tests.cc", ":bridge/source", - ":module/source", ":extra/source", + ":module/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -41,11 +41,11 @@ rust_cxx_bridge( ) rust_cxx_bridge( - name = "module", - src = "ffi/module.rs", + name = "extra", + src = "ffi/extra.rs", ) rust_cxx_bridge( - name = "extra", - src = "ffi/extra.rs", + name = "module", + src = "ffi/module.rs", ) diff --git a/tests/BUILD b/tests/BUILD index 4dff0a0..233926b 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -27,8 +27,8 @@ cc_library( srcs = [ "ffi/tests.cc", ":bridge/source", - ":module/source", ":extra/source", + ":module/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -44,13 +44,13 @@ rust_cxx_bridge( ) rust_cxx_bridge( - name = "module", - src = "ffi/module.rs", + name = "extra", + src = "ffi/extra.rs", deps = [":impl"], ) rust_cxx_bridge( - name = "extra", - src = "ffi/extra.rs", + name = "module", + src = "ffi/module.rs", deps = [":impl"], ) diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index cd6ffe8..a809ea4 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -1,19 +1,15 @@ -// Separate mod so that &self in the lib.rs mod -// has an unambiguous receiver. -// At the moment, the cxx C++ codegen can't convert -// more than one cxx::bridge mod per file, so that's why -// we need to put this outside of lib.rs. -// All of this could go into module.rs instead, but -// for now its purpose is narrowly scoped for testing -// aliasing between cxx::bridge mods, so we'll keep it that -// way and start a new mod here. +// Separate mod so that &self in the lib.rs mod has an unambiguous receiver. At +// the moment, the cxx C++ codegen can't convert more than one cxx::bridge mod +// per file, so that's why we need to put this outside of lib.rs. All of this +// could go into module.rs instead, but for now its purpose is narrowly scoped +// for testing aliasing between cxx::bridge mods, so we'll keep it that way and +// start a new mod here. // Rustfmt mangles the extern type alias. // https://github.com/rust-lang/rustfmt/issues/4159 #[rustfmt::skip] #[cxx::bridge(namespace = tests)] pub mod ffi2 { - impl UniquePtr {} impl UniquePtr {} @@ -32,4 +28,4 @@ pub mod ffi2 { fn c_return_trivial() -> D; fn c_return_opaque_ptr() -> UniquePtr; } -} \ No newline at end of file +} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index a0a2a73..7cffded 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,8 +4,8 @@ clippy::trivially_copy_pass_by_ref )] -pub mod module; pub mod extra; +pub mod module; use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; diff --git a/tests/test.rs b/tests/test.rs index 9828429..a9cd8e1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,5 @@ -use cxx_test_suite::ffi; use cxx_test_suite::extra::ffi2; +use cxx_test_suite::ffi; use std::cell::Cell; use std::ffi::CStr; From 65b66f2091b144ca255bcad0bdf442d483e10a4c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 09:36:16 +0000 Subject: [PATCH 1034/2232] Factor out common symlink check build script --- diff --git a/gen/build/build.rs b/gen/build/build.rs index b8ff9dd..c53bef7 100644 --- a/gen/build/build.rs +++ b/gen/build/build.rs @@ -1,29 +1 @@ -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -const NOSYMLINK: &str = " -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When building `cxx` from a git clone, git's symlink support needs -to be enabled on platforms that have it off by default (Windows). -Either use: - - $ git config --global core.symlinks true - -prior to cloning, or else use: - - $ git clone -c core.symlinks=true ... - -for the clone. - -Symlinks are only required for local development, not for building -`cxx` as a (possibly transitive) dependency from crates.io. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"; - -fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); - } -} +include!("../../tools/cargo/build.rs"); diff --git a/gen/cmd/build.rs b/gen/cmd/build.rs index b8ff9dd..c53bef7 100644 --- a/gen/cmd/build.rs +++ b/gen/cmd/build.rs @@ -1,29 +1 @@ -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -const NOSYMLINK: &str = " -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When building `cxx` from a git clone, git's symlink support needs -to be enabled on platforms that have it off by default (Windows). -Either use: - - $ git config --global core.symlinks true - -prior to cloning, or else use: - - $ git clone -c core.symlinks=true ... - -for the clone. - -Symlinks are only required for local development, not for building -`cxx` as a (possibly transitive) dependency from crates.io. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"; - -fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); - } -} +include!("../../tools/cargo/build.rs"); diff --git a/gen/lib/build.rs b/gen/lib/build.rs index b8ff9dd..c53bef7 100644 --- a/gen/lib/build.rs +++ b/gen/lib/build.rs @@ -1,29 +1 @@ -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -const NOSYMLINK: &str = " -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When building `cxx` from a git clone, git's symlink support needs -to be enabled on platforms that have it off by default (Windows). -Either use: - - $ git config --global core.symlinks true - -prior to cloning, or else use: - - $ git clone -c core.symlinks=true ... - -for the clone. - -Symlinks are only required for local development, not for building -`cxx` as a (possibly transitive) dependency from crates.io. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"; - -fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); - } -} +include!("../../tools/cargo/build.rs"); diff --git a/macro/build.rs b/macro/build.rs index b8ff9dd..927f72b 100644 --- a/macro/build.rs +++ b/macro/build.rs @@ -1,29 +1 @@ -use std::io::{self, Write}; -use std::path::Path; -use std::process; - -const NOSYMLINK: &str = " -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When building `cxx` from a git clone, git's symlink support needs -to be enabled on platforms that have it off by default (Windows). -Either use: - - $ git config --global core.symlinks true - -prior to cloning, or else use: - - $ git clone -c core.symlinks=true ... - -for the clone. - -Symlinks are only required for local development, not for building -`cxx` as a (possibly transitive) dependency from crates.io. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"; - -fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); - } -} +include!("../tools/cargo/build.rs"); diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs new file mode 100644 index 0000000..b8ff9dd --- /dev/null +++ b/tools/cargo/build.rs @@ -0,0 +1,29 @@ +use std::io::{self, Write}; +use std::path::Path; +use std::process; + +const NOSYMLINK: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone, git's symlink support needs +to be enabled on platforms that have it off by default (Windows). +Either use: + + $ git config --global core.symlinks true + +prior to cloning, or else use: + + $ git clone -c core.symlinks=true ... + +for the clone. + +Symlinks are only required for local development, not for building +`cxx` as a (possibly transitive) dependency from crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + +fn main() { + if !Path::new("src/syntax/mod.rs").exists() { + let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); + process::exit(1); + } +} From 188d9f1256e6a46b4088599ed8bcbf6999479dce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 09:37:51 +0000 Subject: [PATCH 1035/2232] Display usable clone command on symlink error --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index b8ff9dd..c0a94bc 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -12,7 +12,7 @@ Either use: prior to cloning, or else use: - $ git clone -c core.symlinks=true ... + $ git clone -c core.symlinks=true https://github.com/dtolnay/cxx for the clone. From a00021934eebadf240862946bac9e75d52ea0d0c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 10:10:34 +0000 Subject: [PATCH 1036/2232] Emphasize difference between git builds vs crates.io --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index c0a94bc..4f2eb8e 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -16,8 +16,10 @@ prior to cloning, or else use: for the clone. -Symlinks are only required for local development, not for building -`cxx` as a (possibly transitive) dependency from crates.io. +Symlinks are only required when compiling locally from a clone of +the git repository---they are NOT required when building `cxx` as +a Cargo-managed (possibly transitive) build dependency downloaded +through crates.io. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "; From c9c197bff912456f1cb09a047cb2b0960e891458 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 10:49:37 +0000 Subject: [PATCH 1037/2232] Try to detect symlinking disabled on Windows --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index 4f2eb8e..f6ec026 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -1,8 +1,12 @@ use std::io::{self, Write}; +#[cfg(windows)] +use std::os::windows::fs as windows; use std::path::Path; use std::process; +#[cfg(windows)] +use std::{env, fs}; -const NOSYMLINK: &str = " +const MISSING: &str = " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When building `cxx` from a git clone, git's symlink support needs to be enabled on platforms that have it off by default (Windows). @@ -23,9 +27,46 @@ through crates.io. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "; +#[cfg(windows)] +const DENIED: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone on Windows we need Developer +Mode enabled for symlink support. + +To enable Developer Mode: go under Settings to Update & Security, +then 'For developers', and turn on the toggle for Developer Mode. + +For more explanation of symlinks in Windows, see these resources: +> https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ +> https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development + +Symlinks are only required when compiling locally from a clone of +the git repository---they are NOT required when building `cxx` as +a Cargo-managed (possibly transitive) build dependency downloaded +through crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); + if Path::new("src/syntax/mod.rs").exists() { + return; } + + #[allow(unused_mut)] + let mut message = MISSING; + + #[cfg(windows)] + if let Some(out_dir) = env::var_os("OUT_DIR") { + let parent_dir = Path::new(&out_dir).join("symlink"); + let from_dir = parent_dir.join("from"); + let to_dir = parent_dir.join("to"); + if fs::create_dir_all(&from_dir).is_ok() + && windows::symlink_dir(&from_dir, &to_dir).is_err() + { + message = DENIED; + } + } + + let _ = io::stderr().write_all(message.as_bytes()); + process::exit(1); } From 2a0a80a260527d509b827107bdb41624c29d2996 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 10:49:37 +0000 Subject: [PATCH 1038/2232] Raise minimum supported Rust version to 1.43 --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 916b6da..b7cad53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,14 +45,6 @@ jobs: - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace --exclude cxx-test-suite - msrv: - name: Rust 1.42.0 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.42.0 - - run: cargo run --manifest-path demo/Cargo.toml - buck: name: Buck runs-on: ubuntu-latest diff --git a/README.md b/README.md index 7171724..7906020 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ cxx = "0.5" cxx-build = "0.5" ``` -*Compiler support: requires rustc 1.42+ and c++11 or newer*
+*Compiler support: requires rustc 1.43+ and c++11 or newer*
*[Release notes](https://github.com/dtolnay/cxx/releases)*
diff --git a/src/lib.rs b/src/lib.rs index 7157c0c..efd3fbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ //! //!
//! -//! *Compiler support: requires rustc 1.42+ and c++11 or newer*
+//! *Compiler support: requires rustc 1.43+ and c++11 or newer*
//! *[Release notes](https://github.com/dtolnay/cxx/releases)* //! //!
From 251e99384f0ebe98150b24b907af346a41b0453e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 11 2020 10:56:41 +0000 Subject: [PATCH 1039/2232] Merge pull request #358 from dtolnay/symlink Try to detect symlinking disabled on Windows --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index 4f2eb8e..f6ec026 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -1,8 +1,12 @@ use std::io::{self, Write}; +#[cfg(windows)] +use std::os::windows::fs as windows; use std::path::Path; use std::process; +#[cfg(windows)] +use std::{env, fs}; -const NOSYMLINK: &str = " +const MISSING: &str = " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When building `cxx` from a git clone, git's symlink support needs to be enabled on platforms that have it off by default (Windows). @@ -23,9 +27,46 @@ through crates.io. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "; +#[cfg(windows)] +const DENIED: &str = " +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When building `cxx` from a git clone on Windows we need Developer +Mode enabled for symlink support. + +To enable Developer Mode: go under Settings to Update & Security, +then 'For developers', and turn on the toggle for Developer Mode. + +For more explanation of symlinks in Windows, see these resources: +> https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ +> https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development + +Symlinks are only required when compiling locally from a clone of +the git repository---they are NOT required when building `cxx` as +a Cargo-managed (possibly transitive) build dependency downloaded +through crates.io. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"; + fn main() { - if !Path::new("src/syntax/mod.rs").exists() { - let _ = io::stderr().lock().write_all(NOSYMLINK.as_bytes()); - process::exit(1); + if Path::new("src/syntax/mod.rs").exists() { + return; } + + #[allow(unused_mut)] + let mut message = MISSING; + + #[cfg(windows)] + if let Some(out_dir) = env::var_os("OUT_DIR") { + let parent_dir = Path::new(&out_dir).join("symlink"); + let from_dir = parent_dir.join("from"); + let to_dir = parent_dir.join("to"); + if fs::create_dir_all(&from_dir).is_ok() + && windows::symlink_dir(&from_dir, &to_dir).is_err() + { + message = DENIED; + } + } + + let _ = io::stderr().write_all(message.as_bytes()); + process::exit(1); } From ccde860981e865972c458371b6de1d756ebeb0fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 12 2020 00:53:50 +0000 Subject: [PATCH 1040/2232] Tweak target dir finding heuristic --- diff --git a/gen/build/src/cargo.rs b/gen/build/src/cargo.rs deleted file mode 100644 index 18b6b6f..0000000 --- a/gen/build/src/cargo.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::paths::TargetDir; -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; -use std::str; - -pub(crate) fn target_dir(out_dir: &Path) -> TargetDir { - try_target_dir(out_dir).map_or(TargetDir::Unknown, TargetDir::Path) -} - -fn try_target_dir(out_dir: &Path) -> Option { - if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { - let target_dir = PathBuf::from(target_dir); - if target_dir.is_absolute() { - return Some(target_dir); - } else { - return None; - }; - } - - let cargo = option_env!("CARGO").unwrap_or("cargo"); - let output = Command::new(cargo) - .current_dir(out_dir) - .arg("metadata") - .arg("--no-deps") - .arg("--format-version=1") - .output() - .ok()?; - - // Cargo only outputs utf8 encoded JSON. - let mut metadata = str::from_utf8(&output.stdout).ok()?; - - let key_pattern = "\"target_directory\":"; - let key_index = metadata.rfind(key_pattern)?; - metadata = &metadata[key_index + key_pattern.len()..]; - let open_quote_index = metadata.find('"')?; - metadata = &metadata[open_quote_index + 1..]; - let close_quote_index = metadata.find('"')?; - let string = &metadata[..close_quote_index]; - let target_directory = string.replace("\\\\", "\\"); - Some(PathBuf::from(target_directory)) -} diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 128d933..e6de985 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -54,18 +54,19 @@ clippy::toplevel_ref_arg )] -mod cargo; mod cfg; mod error; mod gen; mod out; mod paths; mod syntax; +mod target; use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::Opt; -use crate::paths::{PathExt, TargetDir}; +use crate::paths::PathExt; +use crate::target::TargetDir; use cc::Build; use std::collections::BTreeMap; use std::env; @@ -135,13 +136,7 @@ impl Project { let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; - let target_dir = match cargo::target_dir(&out_dir) { - target_dir @ TargetDir::Path(_) => target_dir, - // Fallback if Cargo did not work. - TargetDir::Unknown => paths::search_parents_for_target_dir(&out_dir), - }; - - let shared_dir = match target_dir { + let shared_dir = match target::find_target_dir(&out_dir) { TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), TargetDir::Unknown => scratch::path("cxxbridge"), }; diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs index 56c0018..4459363 100644 --- a/gen/build/src/paths.rs +++ b/gen/build/src/paths.rs @@ -3,11 +3,6 @@ use crate::gen::fs; use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; -pub(crate) enum TargetDir { - Path(PathBuf), - Unknown, -} - pub(crate) fn manifest_dir() -> Result { crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from) } @@ -44,34 +39,6 @@ impl PathExt for Path { } } -pub(crate) fn search_parents_for_target_dir(out_dir: &Path) -> TargetDir { - // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to - // handle in includes. - // https://github.com/rust-lang/rust/issues/42869 - // https://github.com/alexcrichton/cc-rs/issues/169 - let mut also_try_canonical = cfg!(not(windows)); - - let mut dir = out_dir.to_owned(); - loop { - let is_target = dir.ends_with("target"); - let parent_contains_cargo_toml = dir.with_file_name("Cargo.toml").exists(); - if is_target && parent_contains_cargo_toml { - return TargetDir::Path(dir); - } - if dir.pop() { - continue; - } - if also_try_canonical { - if let Ok(canonical_dir) = out_dir.canonicalize() { - dir = canonical_dir; - also_try_canonical = false; - continue; - } - } - return TargetDir::Unknown; - } -} - #[cfg(unix)] pub(crate) use self::fs::symlink_file as symlink_or_copy; diff --git a/gen/build/src/target.rs b/gen/build/src/target.rs new file mode 100644 index 0000000..58ada3a --- /dev/null +++ b/gen/build/src/target.rs @@ -0,0 +1,42 @@ +use std::env; +use std::path::{Path, PathBuf}; + +pub(crate) enum TargetDir { + Path(PathBuf), + Unknown, +} + +pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir { + if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { + let target_dir = PathBuf::from(target_dir); + if target_dir.is_absolute() { + return TargetDir::Path(target_dir); + } else { + return TargetDir::Unknown; + }; + } + + // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to + // handle in includes. + // https://github.com/rust-lang/rust/issues/42869 + // https://github.com/alexcrichton/cc-rs/issues/169 + let mut also_try_canonical = cfg!(not(windows)); + + let mut dir = out_dir.to_owned(); + loop { + if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() { + return TargetDir::Path(dir); + } + if dir.pop() { + continue; + } + if also_try_canonical { + if let Ok(canonical_dir) = out_dir.canonicalize() { + dir = canonical_dir; + also_try_canonical = false; + continue; + } + } + return TargetDir::Unknown; + } +} From 9e457018915999ca26b372e9291a0793f0b5fdaa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 12 2020 04:55:48 +0000 Subject: [PATCH 1041/2232] Set default flavor of buck rust library build operations Previously e.g. `buck build :cxx` was surprisingly a no-op, requiring one to build `:cxx#check` or `:cxx#rlib-pic` in order to perform any compiler invocation at all. defaults.rust_library.type is a recent feature (landed in facebook/buck@c425eb93e1d4aa7768e40a7bccf8601698c5cddd), hence the bump of the Buck commit pulled in for CI. --- diff --git a/.buckconfig b/.buckconfig index 3e5092d..5c6c995 100644 --- a/.buckconfig +++ b/.buckconfig @@ -18,3 +18,6 @@ -Clink-arg=-fuse-ld=lld \ -Crelocation-model=dynamic-no-pic \ --cap-lints=allow + +[defaults.rust_library] + type = check diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7cad53..50c9b65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: Install Buck run: | mkdir bin - wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex # dev branch from 2020.09.21 + wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/a5f0342ae3/buck-a5f0342ae3-java11.pex # dev branch from 2020.10.11 chmod +x bin/buck echo bin >> $GITHUB_PATH - name: Install lld From bd03ae4cd1d9e75ba611e1a02761a50f26335b97 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 12 2020 05:30:28 +0000 Subject: [PATCH 1042/2232] Update buck in devcontainer to match Actions --- diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile index d4ab609..6085459 100644 --- a/.devcontainer/build.Dockerfile +++ b/.devcontainer/build.Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update \ && rustup default nightly 2>&1 \ && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \ && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \ - && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/891556d066/buck-891556d066-java11.pex \ + && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/a5f0342ae3/buck-a5f0342ae3-java11.pex \ && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \ && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \ && chmod +x bin/install-bazel bin/buck bin/buildifier \ From e35673d7debe16222c1667131e51969c093e225c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 12 2020 05:31:51 +0000 Subject: [PATCH 1043/2232] Add VS Code C++ tools --- diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fef25d4..b8deba2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,6 +14,7 @@ "extensions": [ "BazelBuild.vscode-bazel", "matklad.rust-analyzer", + "ms-vscode.cpptools", "vadimcn.vscode-lldb" ] } From d75f7e2911f761ee792bc631fac70da18eff0bbd Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 12 2020 23:16:19 +0000 Subject: [PATCH 1044/2232] Allow creation of UniquePtrs to trivial aliased types. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 5d47c63..3cd94d1 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1245,7 +1245,12 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { let instance = to_mangled(&out.namespace, ty); let can_construct_from_value = match ty { - Type::Ident(ident) => types.structs.contains_key(ident), + // Some aliases are to opaque types; some are to trivial types. + // We can't know at code generation time, so we generate both C++ + // and Rust side bindings for a "new" method anyway. But that + // Rust code will explode at runtime if anyone tries to call it on + // an opaque type. + Type::Ident(ident) => types.structs.contains_key(ident) || types.aliases.contains_key(ident), _ => false, }; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6fa7ea7..4213b4d 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -814,13 +814,22 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) { + let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { + let trivial_assertion: Option = if types.aliases.contains_key(ident) { + Some(parse_quote! { + < < #ident as :: cxx :: ExternType > :: Kind as :: cxx :: kind :: Kind > :: assert_trivial(); + }) + } else { + None + }; + Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); } + #trivial_assertion let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); unsafe { __new(&mut repr, &mut value) } repr diff --git a/src/extern_type.rs b/src/extern_type.rs index b9c5386..45414c8 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -165,9 +165,17 @@ pub mod kind { /// indirection. pub enum Trivial {} - pub trait Kind: private::Sealed {} - impl Kind for Opaque {} - impl Kind for Trivial {} + pub trait Kind: private::Sealed { + fn assert_trivial(); + } + impl Kind for Opaque { + fn assert_trivial() { + panic!("Type not trivial"); + } + } + impl Kind for Trivial { + fn assert_trivial() {} + } } mod private { diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index a809ea4..6209702 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -12,12 +12,14 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} + impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); type D = crate::other::D; type E = crate::other::E; + type F = crate::other::F; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 7cffded..f80cda7 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -14,15 +14,23 @@ mod other { use cxx::kind::{Opaque, Trivial}; use cxx::{type_id, CxxString, ExternType}; + // Trivial. #[repr(C)] pub struct D { - d: u64, + pub d: u64, } + // Opaque, and has realistic complexity. #[repr(C)] pub struct E { - e: u64, e_str: CxxString, + e: u64, + } + + // Opaque, but simple enough that bad code can try to create it. + #[repr(C)] + pub struct F { + pub f: u64, } unsafe impl ExternType for D { @@ -34,6 +42,11 @@ mod other { type Id = type_id!("tests::E"); type Kind = Opaque; } + + unsafe impl ExternType for F { + type Id = type_id!("tests::F"); + type Kind = Opaque; + } } #[cxx::bridge(namespace = tests)] diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index b3f547e..ff1fbd3 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -37,6 +37,10 @@ struct E { std::string e_str; }; +struct F { + uint64_t f; +}; + enum COwnedEnum { CVal1, CVal2, diff --git a/tests/test.rs b/tests/test.rs index a9cd8e1..3ea02fa 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -199,6 +199,7 @@ fn test_extern_trivial() { check!(ffi2::c_take_trivial(d)); let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); + cxx::UniquePtr::new(ffi2::D { d: 42 }); } #[test] @@ -206,4 +207,7 @@ fn test_extern_opaque() { let e = ffi2::c_return_opaque_ptr(); check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); check!(ffi2::c_take_opaque_ptr(e)); + assert!(std::panic::catch_unwind(|| { + cxx::UniquePtr::new(ffi2::F { f: 42 }) + }).is_err()); } From 117d3baac74bd577833ab09656bd294a212a7755 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 12 2020 23:33:30 +0000 Subject: [PATCH 1045/2232] Bazel/Buck test build fixups. Sacrifices to satisfy the PR builder guardians. --- diff --git a/tests/BUCK b/tests/BUCK index 0ddf1a8..5bbe500 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -3,7 +3,10 @@ load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", srcs = ["test.rs"], - deps = [":ffi"], + deps = [ + ":ffi", + "//:cxx", + ], ) rust_library( diff --git a/tests/BUILD b/tests/BUILD index 233926b..57ffab9 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -6,7 +6,10 @@ rust_test( name = "test", size = "small", srcs = ["test.rs"], - deps = [":cxx_test_suite"], + deps = [ + ":cxx_test_suite", + "//:cxx", + ], ) rust_library( From 9f7ff2e3a619089a6ae6ff8cc83425e5c1f493e0 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 13 2020 03:14:17 +0000 Subject: [PATCH 1046/2232] Switch to build-time squashing of 'new' method. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3cd94d1..9b4e120 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1247,9 +1247,9 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. // We can't know at code generation time, so we generate both C++ - // and Rust side bindings for a "new" method anyway. But that - // Rust code will explode at runtime if anyone tries to call it on - // an opaque type. + // and Rust side bindings for a "new" method anyway. But the Rust + // code can't be called for Opaque types because the 'new' + // method is not implemented. Type::Ident(ident) => types.structs.contains_key(ident) || types.aliases.contains_key(ident), _ => false, }; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4213b4d..4a4ec64 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -815,21 +815,12 @@ fn expand_unique_ptr( let link_drop = format!("{}drop", prefix); let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { - let trivial_assertion: Option = if types.aliases.contains_key(ident) { - Some(parse_quote! { - < < #ident as :: cxx :: ExternType > :: Kind as :: cxx :: kind :: Kind > :: assert_trivial(); - }) - } else { - None - }; - Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); } - #trivial_assertion let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); unsafe { __new(&mut repr, &mut value) } repr diff --git a/src/extern_type.rs b/src/extern_type.rs index 45414c8..b9c5386 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -165,17 +165,9 @@ pub mod kind { /// indirection. pub enum Trivial {} - pub trait Kind: private::Sealed { - fn assert_trivial(); - } - impl Kind for Opaque { - fn assert_trivial() { - panic!("Type not trivial"); - } - } - impl Kind for Trivial { - fn assert_trivial() {} - } + pub trait Kind: private::Sealed {} + impl Kind for Opaque {} + impl Kind for Trivial {} } mod private { diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 477c716..4bb5d67 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,5 +1,7 @@ use crate::cxx_string::CxxString; use crate::cxx_vector::{self, CxxVector, VectorElement}; +use crate::ExternType; +use crate::kind::Trivial; use core::ffi::c_void; use core::fmt::{self, Debug, Display}; use core::marker::PhantomData; @@ -32,7 +34,9 @@ where } /// Allocates memory on the heap and makes a UniquePtr pointing to it. - pub fn new(value: T) -> Self { + pub fn new(value: T) -> Self + where + T: ExternType { UniquePtr { repr: T::__new(value), ty: PhantomData, diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 6209702..a809ea4 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -12,14 +12,12 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} - impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); type D = crate::other::D; type E = crate::other::E; - type F = crate::other::F; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index f80cda7..b3c2330 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -14,25 +14,17 @@ mod other { use cxx::kind::{Opaque, Trivial}; use cxx::{type_id, CxxString, ExternType}; - // Trivial. #[repr(C)] pub struct D { pub d: u64, } - // Opaque, and has realistic complexity. #[repr(C)] pub struct E { e_str: CxxString, e: u64, } - // Opaque, but simple enough that bad code can try to create it. - #[repr(C)] - pub struct F { - pub f: u64, - } - unsafe impl ExternType for D { type Id = type_id!("tests::D"); type Kind = Trivial; @@ -42,11 +34,6 @@ mod other { type Id = type_id!("tests::E"); type Kind = Opaque; } - - unsafe impl ExternType for F { - type Id = type_id!("tests::F"); - type Kind = Opaque; - } } #[cxx::bridge(namespace = tests)] diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index ff1fbd3..b3f547e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -37,10 +37,6 @@ struct E { std::string e_str; }; -struct F { - uint64_t f; -}; - enum COwnedEnum { CVal1, CVal2, diff --git a/tests/test.rs b/tests/test.rs index 3ea02fa..b92eebf 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -207,7 +207,4 @@ fn test_extern_opaque() { let e = ffi2::c_return_opaque_ptr(); check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); check!(ffi2::c_take_opaque_ptr(e)); - assert!(std::panic::catch_unwind(|| { - cxx::UniquePtr::new(ffi2::F { f: 42 }) - }).is_err()); } diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs new file mode 100644 index 0000000..6f3bf8c --- /dev/null +++ b/tests/ui/unique_ptr_to_opaque.rs @@ -0,0 +1,26 @@ +mod outside { + #[repr(C)] + pub struct C { + pub a: u8, + } + unsafe impl cxx::ExternType for C { + type Id = cxx::type_id!("C"); + type Kind = cxx::kind::Opaque; + } +} + + +#[cxx::bridge] +mod ffi { + impl UniquePtr {} + + extern "C" { + type C = crate::outside::C; + } + + impl UniquePtr {} +} + +fn main() { + cxx::UniquePtr::new(outside::C { a: 4 } ); +} diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr new file mode 100644 index 0000000..5525a05 --- /dev/null +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -0,0 +1,7 @@ +error[E0271]: type mismatch resolving `::Kind == Trivial` + --> $DIR/unique_ptr_to_opaque.rs:25:5 + | +25 | cxx::UniquePtr::new(outside::C { a: 4 } ); + | ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` + | + = note: required by `UniquePtr::::new` From 595ac276322c056d093805dab9f559bad1234dd6 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Oct 13 2020 20:48:08 +0000 Subject: [PATCH 1047/2232] Fix Windows symlink test to give reproducible error messages After a `git clone -c core.symlinks=false` and `cargo test`, the script outputs a Git error as expected. When running `cargo test` again, the error message changes to "symlink support needs to be enabled". The test is done for an already existing directory, and the Windows mklink command _fails_ when the symlink exists already. This commit deletes the link prior to the symlink creation. --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index f6ec026..6c9d22d 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -61,6 +61,7 @@ fn main() { let from_dir = parent_dir.join("from"); let to_dir = parent_dir.join("to"); if fs::create_dir_all(&from_dir).is_ok() + && fs::remove_dir(&to_dir).is_ok() && windows::symlink_dir(&from_dir, &to_dir).is_err() { message = DENIED; From 1137642ce4360b3d8832ab5c469fb7e95faaa256 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 13 2020 21:17:31 +0000 Subject: [PATCH 1048/2232] Merge pull request #362 from Bromeon/bugfix/symlink-check Fix Windows symlink test to give reproducible error messages --- diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs index f6ec026..6c9d22d 100644 --- a/tools/cargo/build.rs +++ b/tools/cargo/build.rs @@ -61,6 +61,7 @@ fn main() { let from_dir = parent_dir.join("from"); let to_dir = parent_dir.join("to"); if fs::create_dir_all(&from_dir).is_ok() + && fs::remove_dir(&to_dir).is_ok() && windows::symlink_dir(&from_dir, &to_dir).is_err() { message = DENIED; From c904f8a74f7a8c37e7dffab47a68433dba9ae4de Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 16 2020 17:47:58 +0000 Subject: [PATCH 1049/2232] Make cxx.h available to high level code generators. At present, this file is available only from the git repository, but higher level code generators may wish to ensure that they supply a version of cxx.h corresponding precisely to the version of cxx in use. Specifically, such higher level code generators may wish to use rust::Str and similar types in code which _they_ autogenerate, and thus need a way to include definitions of such types. As this code is autogenerated, it can't reasonably rummage around the cxx git repository to find the correct cxx.h header. To be even more specific, higher level code generators may wish to pass rust::Str and/or rust::String types into C++, in order to create UniquePtrs from Rust strings during function calls. --- diff --git a/gen/README.md b/gen/README.md index 9786911..50adeca 100644 --- a/gen/README.md +++ b/gen/README.md @@ -2,3 +2,6 @@ This directory contains CXX's C++ code generator. This code generator has two public frontends, one a command-line application (binary) in the *cmd* directory and the other a library intended to be used from a build.rs in the *build* directory. + +There's also a 'lib' frontend which is intended to allow higher level code generators +to embed cxx. This is not yet recommended for general use. diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index dae7e78..e4d9e71 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -22,6 +22,7 @@ mod syntax; pub use crate::error::Error; pub use crate::gen::{GeneratedCode, Opt}; use proc_macro2::TokenStream; +use gen::include::HEADER; /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. @@ -31,3 +32,8 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result &'static str { + HEADER +} From 0e01d6406f3591147c1b05de66180d6bc118d3a8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:06:48 +0000 Subject: [PATCH 1050/2232] Expose cxx_gen::HEADER as a static &'static str --- diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index e4d9e71..ecfa436 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -20,9 +20,9 @@ mod gen; mod syntax; pub use crate::error::Error; +pub use crate::gen::include::HEADER; pub use crate::gen::{GeneratedCode, Opt}; use proc_macro2::TokenStream; -use gen::include::HEADER; /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. @@ -32,8 +32,3 @@ pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result &'static str { - HEADER -} diff --git a/gen/src/include.rs b/gen/src/include.rs index 1688a4e..3255902 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,6 +1,7 @@ use crate::gen::out::OutFile; use std::fmt::{self, Display}; +/// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) { From 33b1ae956497b2f4d320c1e26ce8e31fd4d52fc5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:11:56 +0000 Subject: [PATCH 1051/2232] Merge pull request 364 from adetaylor/make-cxx-h-available --- diff --git a/gen/README.md b/gen/README.md index 9786911..d00b98f 100644 --- a/gen/README.md +++ b/gen/README.md @@ -2,3 +2,6 @@ This directory contains CXX's C++ code generator. This code generator has two public frontends, one a command-line application (binary) in the *cmd* directory and the other a library intended to be used from a build.rs in the *build* directory. + +There's also a 'lib' frontend which is intended to allow higher level code +generators to embed cxx. This is not yet recommended for general use. diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index dae7e78..ecfa436 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -20,6 +20,7 @@ mod gen; mod syntax; pub use crate::error::Error; +pub use crate::gen::include::HEADER; pub use crate::gen::{GeneratedCode, Opt}; use proc_macro2::TokenStream; diff --git a/gen/src/include.rs b/gen/src/include.rs index 1688a4e..3255902 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,6 +1,7 @@ use crate::gen::out::OutFile; use std::fmt::{self, Display}; +/// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) { From 35b3cd359b976138c9ad5c08a7459b8068d047a0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:15:40 +0000 Subject: [PATCH 1052/2232] Merge pull request #361 from adetaylor/trivial-alias-uniqueptr Allow creation of UniquePtrs to trivial aliased types. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 5d47c63..9b4e120 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1245,7 +1245,12 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { let instance = to_mangled(&out.namespace, ty); let can_construct_from_value = match ty { - Type::Ident(ident) => types.structs.contains_key(ident), + // Some aliases are to opaque types; some are to trivial types. + // We can't know at code generation time, so we generate both C++ + // and Rust side bindings for a "new" method anyway. But the Rust + // code can't be called for Opaque types because the 'new' + // method is not implemented. + Type::Ident(ident) => types.structs.contains_key(ident) || types.aliases.contains_key(ident), _ => false, }; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6fa7ea7..4a4ec64 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -814,7 +814,7 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) { + let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 477c716..4bb5d67 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,5 +1,7 @@ use crate::cxx_string::CxxString; use crate::cxx_vector::{self, CxxVector, VectorElement}; +use crate::ExternType; +use crate::kind::Trivial; use core::ffi::c_void; use core::fmt::{self, Debug, Display}; use core::marker::PhantomData; @@ -32,7 +34,9 @@ where } /// Allocates memory on the heap and makes a UniquePtr pointing to it. - pub fn new(value: T) -> Self { + pub fn new(value: T) -> Self + where + T: ExternType { UniquePtr { repr: T::__new(value), ty: PhantomData, diff --git a/tests/BUCK b/tests/BUCK index 0ddf1a8..5bbe500 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -3,7 +3,10 @@ load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( name = "test", srcs = ["test.rs"], - deps = [":ffi"], + deps = [ + ":ffi", + "//:cxx", + ], ) rust_library( diff --git a/tests/BUILD b/tests/BUILD index 233926b..57ffab9 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -6,7 +6,10 @@ rust_test( name = "test", size = "small", srcs = ["test.rs"], - deps = [":cxx_test_suite"], + deps = [ + ":cxx_test_suite", + "//:cxx", + ], ) rust_library( diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 7cffded..b3c2330 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -16,13 +16,13 @@ mod other { #[repr(C)] pub struct D { - d: u64, + pub d: u64, } #[repr(C)] pub struct E { - e: u64, e_str: CxxString, + e: u64, } unsafe impl ExternType for D { diff --git a/tests/test.rs b/tests/test.rs index a9cd8e1..b92eebf 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -199,6 +199,7 @@ fn test_extern_trivial() { check!(ffi2::c_take_trivial(d)); let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); + cxx::UniquePtr::new(ffi2::D { d: 42 }); } #[test] diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs new file mode 100644 index 0000000..6f3bf8c --- /dev/null +++ b/tests/ui/unique_ptr_to_opaque.rs @@ -0,0 +1,26 @@ +mod outside { + #[repr(C)] + pub struct C { + pub a: u8, + } + unsafe impl cxx::ExternType for C { + type Id = cxx::type_id!("C"); + type Kind = cxx::kind::Opaque; + } +} + + +#[cxx::bridge] +mod ffi { + impl UniquePtr {} + + extern "C" { + type C = crate::outside::C; + } + + impl UniquePtr {} +} + +fn main() { + cxx::UniquePtr::new(outside::C { a: 4 } ); +} diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr new file mode 100644 index 0000000..5525a05 --- /dev/null +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -0,0 +1,7 @@ +error[E0271]: type mismatch resolving `::Kind == Trivial` + --> $DIR/unique_ptr_to_opaque.rs:25:5 + | +25 | cxx::UniquePtr::new(outside::C { a: 4 } ); + | ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` + | + = note: required by `UniquePtr::::new` From ca0f9da8320fe77a4044f9a9d0a4e69047dd9789 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:18:54 +0000 Subject: [PATCH 1053/2232] Format PR 361 with rustfmt --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9b4e120..3184604 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1245,12 +1245,13 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { let instance = to_mangled(&out.namespace, ty); let can_construct_from_value = match ty { - // Some aliases are to opaque types; some are to trivial types. - // We can't know at code generation time, so we generate both C++ - // and Rust side bindings for a "new" method anyway. But the Rust - // code can't be called for Opaque types because the 'new' - // method is not implemented. - Type::Ident(ident) => types.structs.contains_key(ident) || types.aliases.contains_key(ident), + // Some aliases are to opaque types; some are to trivial types. We can't + // know at code generation time, so we generate both C++ and Rust side + // bindings for a "new" method anyway. But the Rust code can't be called + // for Opaque types because the 'new' method is not implemented. + Type::Ident(ident) => { + types.structs.contains_key(ident) || types.aliases.contains_key(ident) + } _ => false, }; diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 4bb5d67..3a5fc66 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -1,7 +1,7 @@ use crate::cxx_string::CxxString; use crate::cxx_vector::{self, CxxVector, VectorElement}; -use crate::ExternType; use crate::kind::Trivial; +use crate::ExternType; use core::ffi::c_void; use core::fmt::{self, Debug, Display}; use core::marker::PhantomData; @@ -36,7 +36,8 @@ where /// Allocates memory on the heap and makes a UniquePtr pointing to it. pub fn new(value: T) -> Self where - T: ExternType { + T: ExternType, + { UniquePtr { repr: T::__new(value), ty: PhantomData, diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs index 6f3bf8c..5ae03f8 100644 --- a/tests/ui/unique_ptr_to_opaque.rs +++ b/tests/ui/unique_ptr_to_opaque.rs @@ -9,7 +9,6 @@ mod outside { } } - #[cxx::bridge] mod ffi { impl UniquePtr {} @@ -22,5 +21,5 @@ mod ffi { } fn main() { - cxx::UniquePtr::new(outside::C { a: 4 } ); + cxx::UniquePtr::new(outside::C { a: 4 }); } diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr index 5525a05..f0e4aa0 100644 --- a/tests/ui/unique_ptr_to_opaque.stderr +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -1,7 +1,7 @@ error[E0271]: type mismatch resolving `::Kind == Trivial` - --> $DIR/unique_ptr_to_opaque.rs:25:5 + --> $DIR/unique_ptr_to_opaque.rs:24:5 | -25 | cxx::UniquePtr::new(outside::C { a: 4 } ); +24 | cxx::UniquePtr::new(outside::C { a: 4 }); | ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` | = note: required by `UniquePtr::::new` From 441956e803c1070c85ecd14d61c443311905ef8f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:21:51 +0000 Subject: [PATCH 1054/2232] Match layout of C++ tests::E and struct E in Rust --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index b3c2330..4078b37 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -21,8 +21,8 @@ mod other { #[repr(C)] pub struct E { - e_str: CxxString, e: u64, + e_str: CxxString, } unsafe impl ExternType for D { From 743caa283ced87d021e67c65ced1860fe0a11ecc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:22:47 +0000 Subject: [PATCH 1055/2232] Remove duplicated UniquePtr impl in ui test --- diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs index 5ae03f8..720ca69 100644 --- a/tests/ui/unique_ptr_to_opaque.rs +++ b/tests/ui/unique_ptr_to_opaque.rs @@ -11,8 +11,6 @@ mod outside { #[cxx::bridge] mod ffi { - impl UniquePtr {} - extern "C" { type C = crate::outside::C; } diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr index f0e4aa0..19d76a3 100644 --- a/tests/ui/unique_ptr_to_opaque.stderr +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -1,7 +1,7 @@ error[E0271]: type mismatch resolving `::Kind == Trivial` - --> $DIR/unique_ptr_to_opaque.rs:24:5 + --> $DIR/unique_ptr_to_opaque.rs:22:5 | -24 | cxx::UniquePtr::new(outside::C { a: 4 }); +22 | cxx::UniquePtr::new(outside::C { a: 4 }); | ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` | = note: required by `UniquePtr::::new` From a32ca3080b0289fd26aa12cb4870b9f6e11663bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:31:09 +0000 Subject: [PATCH 1056/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index 93abbdc..d07cda2 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -72,7 +72,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.43/src/**"]), + srcs = glob(["vendor/syn-1.0.44/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index f798190..a539bd2 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -78,7 +78,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.43/src/**"]), + srcs = glob(["vendor/syn-1.0.44/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 54f250e..b85924f 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -230,18 +230,18 @@ checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" [[package]] name = "serde" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -250,9 +250,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -267,9 +267,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2e59c50ed8f6b050b071aa7b6865293957a9af6b58b94f97c1c9434ad440ea" +checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" dependencies = [ "proc-macro2", "quote", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] From e37ce2f4ee65a261ee64189585a5615a459c6403 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 16 2020 20:32:07 +0000 Subject: [PATCH 1057/2232] Release 0.5.2 --- diff --git a/Cargo.toml b/Cargo.toml index 045ecc3..4e01e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "0.5.1" # remember to update html_root_url +version = "0.5.2" # remember to update html_root_url authors = ["David Tolnay "] edition = "2018" links = "cxxbridge05" @@ -20,15 +20,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=0.5.1", path = "macro" } +cxxbridge-macro = { version = "=0.5.2", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=0.5.1", path = "flags", default-features = false } +cxxbridge-flags = { version = "=0.5.2", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=0.5.1", path = "gen/build" } +cxx-build = { version = "=0.5.2", path = "gen/build" } cxx-gen = { version = "0.5", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" diff --git a/flags/Cargo.toml b/flags/Cargo.toml index f8e5a7b..2f4c512 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "0.5.1" +version = "0.5.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index df6eea0..b1954b4 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "0.5.1" +version = "0.5.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 99addee..f1c977c 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "0.5.1" +version = "0.5.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index 18845f8..ee62e14 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.5.1" +version = "0.5.2" authors = ["Adrian Taylor "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 1618b0d..3d12d20 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "0.5.1" +version = "0.5.2" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index efd3fbc..9cd3adf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/0.5.1")] +#![doc(html_root_url = "https://docs.rs/cxx/0.5.2")] #![deny(improper_ctypes)] #![allow(non_camel_case_types)] #![allow( diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index b85924f..3a32f05 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -59,7 +59,7 @@ dependencies = [ [[package]] name = "cxx" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cc", "cxx-build", @@ -74,7 +74,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cc", "codespan-reporting", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cc", "codespan-reporting", @@ -108,7 +108,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "0.5.1" +version = "0.5.2" dependencies = [ "clap", "codespan-reporting", @@ -119,11 +119,11 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "0.5.1" +version = "0.5.2" [[package]] name = "cxxbridge-macro" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cxx", "proc-macro2", From cca5215060cb472902d52fba9414fdd0ea9009d9 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 23 2020 00:56:53 +0000 Subject: [PATCH 1058/2232] Diagnostic improvement. --- diff --git a/syntax/check.rs b/syntax/check.rs index 2f3c334..ced1570 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -67,7 +67,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, "unsupported type"); + cx.error(ident, &format!("unsupported type: {}", ident)); } } From 9a158e408fc916255be0dbf88cf39d1d6176d548 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1059/2232] Adding tests for types in namespaces. --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4078b37..621608e 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -49,6 +49,32 @@ pub mod ffi { CVal, } + #[namespace(namespace = A)] + #[derive(Clone)] + struct AShared { + z: usize, + } + + #[namespace(namespace = A)] + enum AEnum { + AAVal, + ABVal = 2020, + ACVal, + } + + #[namespace(namespace = A::B)] + enum ABEnum { + ABAVal, + ABBVal = 2020, + ABCVal, + } + + #[namespace(namespace = A::B)] + #[derive(Clone)] + struct ABShared { + z: usize, + } + extern "C" { include!("tests/ffi/tests.h"); @@ -78,6 +104,10 @@ pub mod ffi { fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; + fn c_return_ns_ref(shared: &AShared) -> &usize; + fn c_return_nested_ns_ref(shared: &ABShared) -> &usize; + fn c_return_ns_enum(n: u16) -> AEnum; + fn c_return_nested_ns_enum(n: u16) -> ABEnum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -108,6 +138,12 @@ pub mod ffi { fn c_take_callback(callback: fn(String) -> usize); */ fn c_take_enum(e: Enum); + fn c_take_ns_enum(e: AEnum); + fn c_take_nested_ns_enum(e: ABEnum); + fn c_take_ns_shared(shared: AShared); + fn c_take_nested_ns_shared(shared: ABShared); + fn c_take_rust_vec_ns_shared(v: Vec); + fn c_take_rust_vec_nested_ns_shared(v: Vec); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 983cf95..2c37397 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -43,6 +43,10 @@ size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } +::A::AShared c_return_ns_shared() { return ::A::AShared{2020}; } + +::A::B::ABShared c_return_nested_ns_shared() { return ::A::B::ABShared{2020}; } + rust::Box c_return_box() { return rust::Box::from_raw(cxx_test_suite_get_box()); } @@ -53,6 +57,10 @@ std::unique_ptr c_return_unique_ptr() { const size_t &c_return_ref(const Shared &shared) { return shared.z; } +const size_t &c_return_ns_ref(const ::A::AShared &shared) { return shared.z; } + +const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) { return shared.z; } + size_t &c_return_mut(Shared &shared) { return shared.z; } rust::Str c_return_str(const Shared &shared) { @@ -144,6 +152,26 @@ Enum c_return_enum(uint16_t n) { } } +::A::AEnum c_return_ns_enum(uint16_t n) { + if (n <= static_cast(::A::AEnum::AAVal)) { + return ::A::AEnum::AAVal; + } else if (n <= static_cast(::A::AEnum::ABVal)) { + return ::A::AEnum::ABVal; + } else { + return ::A::AEnum::ACVal; + } +} + +::A::B::ABEnum c_return_nested_ns_enum(uint16_t n) { + if (n <= static_cast(::A::B::ABEnum::ABAVal)) { + return ::A::B::ABEnum::ABAVal; + } else if (n <= static_cast(::A::B::ABEnum::ABBVal)) { + return ::A::B::ABEnum::ABBVal; + } else { + return ::A::B::ABEnum::ABCVal; + } +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -156,6 +184,18 @@ void c_take_shared(Shared shared) { } } +void c_take_ns_shared(::A::AShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_nested_ns_shared(::A::B::ABShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} + void c_take_box(rust::Box r) { if (cxx_test_suite_r_is_correct(&*r)) { cxx_test_suite_set_correct(); @@ -258,6 +298,26 @@ void c_take_rust_vec_shared(rust::Vec v) { } } +void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v) { + uint32_t sum = 0; + for (auto i : v) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v) { + uint32_t sum = 0; + for (auto i : v) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + void c_take_rust_vec_string(rust::Vec v) { (void)v; cxx_test_suite_set_correct(); @@ -326,6 +386,18 @@ void c_take_enum(Enum e) { } } +void c_take_ns_enum(::A::AEnum e) { + if (e == ::A::AEnum::AAVal) { + cxx_test_suite_set_correct(); + } +} + +void c_take_nested_ns_enum(::A::B::ABEnum e) { + if (e == ::A::B::ABEnum::ABAVal) { + cxx_test_suite_set_correct(); + } +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index b3f547e..70d0a43 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -3,6 +3,15 @@ #include #include +namespace A { + struct AShared; + enum class AEnum : uint16_t; + namespace B { + struct ABShared; + enum class ABEnum : uint16_t; + } // namespace B +} // namespace A + namespace tests { struct R; @@ -44,9 +53,13 @@ enum COwnedEnum { size_t c_return_primitive(); Shared c_return_shared(); +::A::AShared c_return_ns_shared(); +::A::B::ABShared c_return_nested_ns_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); const size_t &c_return_ref(const Shared &shared); +const size_t &c_return_ns_ref(const ::A::AShared &shared); +const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared); size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); @@ -66,9 +79,13 @@ rust::Vec c_return_rust_vec_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); +::A::AEnum c_return_ns_enum(uint16_t n); +::A::B::ABEnum c_return_nested_ns_enum(uint16_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); +void c_take_ns_shared(::A::AShared shared); +void c_take_nested_ns_shared(::A::B::ABShared shared); void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); @@ -86,6 +103,8 @@ void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v); +void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v); void c_take_rust_vec_string(rust::Vec v); void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); @@ -98,6 +117,8 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v); void c_take_callback(rust::Fn callback); */ void c_take_enum(Enum e); +void c_take_ns_enum(::A::AEnum e); +void c_take_nested_ns_enum(::A::B::ABEnum e); void c_try_return_void(); size_t c_try_return_primitive(); diff --git a/tests/test.rs b/tests/test.rs index b92eebf..eece7ff 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -23,12 +23,16 @@ macro_rules! check { #[test] fn test_c_return() { let shared = ffi::Shared { z: 2020 }; + let ns_shared = ffi::AShared { z: 2020 }; + let nested_ns_shared = ffi::ABShared { z: 2020 }; assert_eq!(2020, ffi::c_return_primitive()); assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, *ffi::c_return_box()); ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); + assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared)); + assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared)); assert_eq!("2020", ffi::c_return_str(&shared)); assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); @@ -64,6 +68,14 @@ fn test_c_return() { enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr), _ => assert!(false), } + match ffi::c_return_ns_enum(0) { + enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr), + _ => assert!(false), + } + match ffi::c_return_nested_ns_enum(0) { + enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr), + _ => assert!(false), + } } #[test] @@ -88,6 +100,8 @@ fn test_c_take() { check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); + check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 })); + check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); @@ -119,7 +133,16 @@ fn test_c_take() { check!(ffi::c_take_ref_rust_vec(&test_vec)); check!(ffi::c_take_ref_rust_vec_index(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); + let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }]; + check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec)); + let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }]; + check!(ffi::c_take_rust_vec_nested_ns_shared( + nested_ns_shared_test_vec + )); + check!(ffi::c_take_enum(ffi::Enum::AVal)); + check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); + check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal)); } /* From 5e79c647696e55934e108397b2e1c3e824ce8079 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1060/2232] Tests for namespaced opaque extern types. --- diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index a809ea4..1faf375 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -12,20 +12,26 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} + impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); type D = crate::other::D; type E = crate::other::E; + #[namespace (namespace = F)] + type F = crate::other::f::F; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); fn c_take_trivial(d: D); fn c_take_opaque_ptr(e: UniquePtr); fn c_take_opaque_ref(e: &E); + fn c_take_opaque_ns_ptr(e: UniquePtr); + fn c_take_opaque_ns_ref(e: &F); fn c_return_trivial_ptr() -> UniquePtr; fn c_return_trivial() -> D; fn c_return_opaque_ptr() -> UniquePtr; + fn c_return_ns_opaque_ptr() -> UniquePtr; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 621608e..f25fd2a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -25,6 +25,22 @@ mod other { e_str: CxxString, } + pub mod f { + use cxx::kind::Opaque; + use cxx::{type_id, CxxString, ExternType}; + + #[repr(C)] + pub struct F { + e: u64, + e_str: CxxString, + } + + unsafe impl ExternType for F { + type Id = type_id!("F::F"); + type Kind = Opaque; + } + } + unsafe impl ExternType for D { type Id = type_id!("tests::D"); type Kind = Trivial; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2c37397..db51435 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -478,12 +478,25 @@ void c_take_opaque_ptr(std::unique_ptr e) { } } +void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f) { + if (f->f == 40) { + cxx_test_suite_set_correct(); + } +} + void c_take_opaque_ref(const E& e) { if (e.e == 40 && e.e_str == "hello") { cxx_test_suite_set_correct(); } } +void c_take_opaque_ns_ref(const ::F::F& f) { + if (f.f == 40 && f.f_str == "hello") { + cxx_test_suite_set_correct(); + } +} + + std::unique_ptr c_return_trivial_ptr() { auto d = std::unique_ptr(new D()); d->d = 30; @@ -503,6 +516,13 @@ std::unique_ptr c_return_opaque_ptr() { return e; } +std::unique_ptr<::F::F> c_return_ns_opaque_ptr() { + auto f = std::unique_ptr<::F::F>(new ::F::F()); + f->f = 40; + f->f_str = std::string("hello"); + return f; +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 70d0a43..4bdf69e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -12,6 +12,13 @@ namespace A { } // namespace B } // namespace A +namespace F { + struct F { + uint64_t f; + std::string f_str; + }; +} + namespace tests { struct R; @@ -137,10 +144,13 @@ void c_take_trivial_ptr(std::unique_ptr d); void c_take_trivial_ref(const D& d); void c_take_trivial(D d); void c_take_opaque_ptr(std::unique_ptr e); +void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f); void c_take_opaque_ref(const E& e); +void c_take_opaque_ns_ref(const ::F::F& f); std::unique_ptr c_return_trivial_ptr(); D c_return_trivial(); std::unique_ptr c_return_opaque_ptr(); +std::unique_ptr<::F::F> c_return_ns_opaque_ptr(); rust::String cOverloadedFunction(int32_t x); rust::String cOverloadedFunction(rust::Str x); diff --git a/tests/test.rs b/tests/test.rs index eece7ff..73bb084 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -230,4 +230,8 @@ fn test_extern_opaque() { let e = ffi2::c_return_opaque_ptr(); check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); check!(ffi2::c_take_opaque_ptr(e)); + + let f = ffi2::c_return_ns_opaque_ptr(); + check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap())); + check!(ffi2::c_take_opaque_ns_ptr(f)); } From 585bb0bc5605ccf28b5a814b5727967b8ee67c83 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1061/2232] Tests for namespaced extern trivial types --- diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 1faf375..0921724 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -13,6 +13,7 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} impl UniquePtr {} + impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); @@ -21,16 +22,23 @@ pub mod ffi2 { type E = crate::other::E; #[namespace (namespace = F)] type F = crate::other::f::F; + #[namespace (namespace = G)] + type G = crate::other::G; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); fn c_take_trivial(d: D); + fn c_take_trivial_ns_ptr(g: UniquePtr); + fn c_take_trivial_ns_ref(g: &G); + fn c_take_trivial_ns(g: G); fn c_take_opaque_ptr(e: UniquePtr); fn c_take_opaque_ref(e: &E); fn c_take_opaque_ns_ptr(e: UniquePtr); fn c_take_opaque_ns_ref(e: &F); fn c_return_trivial_ptr() -> UniquePtr; fn c_return_trivial() -> D; + fn c_return_trivial_ns_ptr() -> UniquePtr; + fn c_return_trivial_ns() -> G; fn c_return_opaque_ptr() -> UniquePtr; fn c_return_ns_opaque_ptr() -> UniquePtr; } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index f25fd2a..1cd92f9 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -41,6 +41,16 @@ mod other { } } + #[repr(C)] + pub struct G { + pub g: u64, + } + + unsafe impl ExternType for G { + type Id = type_id!("G::G"); + type Kind = Trivial; + } + unsafe impl ExternType for D { type Id = type_id!("tests::D"); type Kind = Trivial; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index db51435..017cb82 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -466,12 +466,32 @@ void c_take_trivial_ref(const D& d) { cxx_test_suite_set_correct(); } } + void c_take_trivial(D d) { if (d.d == 30) { cxx_test_suite_set_correct(); } } + +void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g) { + if (g->g == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ns_ref(const ::G::G& g) { + if (g.g == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ns(::G::G g) { + if (g.g == 30) { + cxx_test_suite_set_correct(); + } +} + void c_take_opaque_ptr(std::unique_ptr e) { if (e->e == 40) { cxx_test_suite_set_correct(); @@ -496,7 +516,6 @@ void c_take_opaque_ns_ref(const ::F::F& f) { } } - std::unique_ptr c_return_trivial_ptr() { auto d = std::unique_ptr(new D()); d->d = 30; @@ -509,6 +528,18 @@ D c_return_trivial() { return d; } +std::unique_ptr<::G::G> c_return_trivial_ns_ptr() { + auto g = std::unique_ptr<::G::G>(new ::G::G()); + g->g = 30; + return g; +} + +::G::G c_return_trivial_ns() { + ::G::G g; + g.g = 30; + return g; +} + std::unique_ptr c_return_opaque_ptr() { auto e = std::unique_ptr(new E()); e->e = 40; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 4bdf69e..7f62672 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -19,6 +19,12 @@ namespace F { }; } +namespace G { + struct G { + uint64_t g; + }; +} + namespace tests { struct R; @@ -143,12 +149,18 @@ const rust::Vec &c_try_return_ref_rust_vec(const C &c); void c_take_trivial_ptr(std::unique_ptr d); void c_take_trivial_ref(const D& d); void c_take_trivial(D d); + +void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g); +void c_take_trivial_ns_ref(const ::G::G& g); +void c_take_trivial_ns(::G::G g); void c_take_opaque_ptr(std::unique_ptr e); void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f); void c_take_opaque_ref(const E& e); void c_take_opaque_ns_ref(const ::F::F& f); std::unique_ptr c_return_trivial_ptr(); D c_return_trivial(); +std::unique_ptr<::G::G> c_return_trivial_ns_ptr(); +::G::G c_return_trivial_ns(); std::unique_ptr c_return_opaque_ptr(); std::unique_ptr<::F::F> c_return_ns_opaque_ptr(); diff --git a/tests/test.rs b/tests/test.rs index 73bb084..02cb494 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -223,6 +223,13 @@ fn test_extern_trivial() { let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); cxx::UniquePtr::new(ffi2::D { d: 42 }); + + let g = ffi2::c_return_trivial_ns(); + check!(ffi2::c_take_trivial_ns_ref(&g)); + check!(ffi2::c_take_trivial_ns(g)); + let g = ffi2::c_return_trivial_ns_ptr(); + check!(ffi2::c_take_trivial_ns_ptr(g)); + cxx::UniquePtr::new(ffi2::G { g: 42 }); } #[test] From d47af7a9d3640438e3d1371a739297b789296cfb Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1062/2232] Tests for opaque C types in namepsaces --- diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 0921724..aa83cc2 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -25,6 +25,9 @@ pub mod ffi2 { #[namespace (namespace = G)] type G = crate::other::G; + #[namespace(namespace = H)] + type H; + fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); fn c_take_trivial(d: D); @@ -41,5 +44,7 @@ pub mod ffi2 { fn c_return_trivial_ns() -> G; fn c_return_opaque_ptr() -> UniquePtr; fn c_return_ns_opaque_ptr() -> UniquePtr; + fn c_return_ns_unique_ptr() -> UniquePtr; + fn c_take_ref_ns_c(h: &H); } } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 017cb82..585a99b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -55,6 +55,10 @@ std::unique_ptr c_return_unique_ptr() { return std::unique_ptr(new C{2020}); } +std::unique_ptr<::H::H> c_return_ns_unique_ptr() { + return std::unique_ptr<::H::H>(new ::H::H{"hello"}); +} + const size_t &c_return_ref(const Shared &shared) { return shared.z; } const size_t &c_return_ns_ref(const ::A::AShared &shared) { return shared.z; } @@ -220,6 +224,12 @@ void c_take_ref_c(const C &c) { } } +void c_take_ref_ns_c(const ::H::H &h) { + if (h.h == "hello") { + cxx_test_suite_set_correct(); + } +} + void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 7f62672..dfedb3c 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -25,6 +25,13 @@ namespace G { }; } +namespace H { + class H { + public: + std::string h; + }; +} + namespace tests { struct R; @@ -70,6 +77,7 @@ Shared c_return_shared(); ::A::B::ABShared c_return_nested_ns_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); +std::unique_ptr<::H::H> c_return_ns_unique_ptr(); const size_t &c_return_ref(const Shared &shared); const size_t &c_return_ns_ref(const ::A::AShared &shared); const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared); @@ -103,6 +111,7 @@ void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); +void c_take_ref_ns_c(const ::H::H &h); void c_take_str(rust::Str s); void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); diff --git a/tests/test.rs b/tests/test.rs index 02cb494..39a700e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -30,6 +30,7 @@ fn test_c_return() { assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, *ffi::c_return_box()); ffi::c_return_unique_ptr(); + ffi2::c_return_ns_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared)); assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared)); @@ -97,6 +98,7 @@ fn test_c_try_return() { #[test] fn test_c_take() { let unique_ptr = ffi::c_return_unique_ptr(); + let unique_ptr_ns = ffi2::c_return_ns_unique_ptr(); check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); @@ -104,6 +106,7 @@ fn test_c_take() { check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); + check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns)); check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); From ddc146ef9f4d67ddf75bf7174fdc1e2a95a2ced5 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1063/2232] Adding tests for functions in other namespaces. --- diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index aa83cc2..633cf93 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -46,5 +46,10 @@ pub mod ffi2 { fn c_return_ns_opaque_ptr() -> UniquePtr; fn c_return_ns_unique_ptr() -> UniquePtr; fn c_take_ref_ns_c(h: &H); + + #[namespace (namespace = other)] + fn ns_c_take_trivial(d: D); + #[namespace (namespace = other)] + fn ns_c_return_trivial() -> D; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 1cd92f9..a491ae2 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -199,6 +199,9 @@ pub mod ffi { fn cOverloadedFunction(x: i32) -> String; #[rust_name = "str_overloaded_function"] fn cOverloadedFunction(x: &str) -> String; + + #[namespace (namespace = other)] + fn ns_c_take_ns_shared(shared: AShared); } extern "C" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 585a99b..1f0f8ff 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -627,3 +627,24 @@ extern "C" const char *cxx_run_test() noexcept { } } // namespace tests + +namespace other { + + void ns_c_take_trivial(::tests::D d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } + } + + ::tests::D ns_c_return_trivial() { + ::tests::D d; + d.d = 30; + return d; + } + + void ns_c_take_ns_shared(::A::AShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } + } +} \ No newline at end of file diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dfedb3c..40bc802 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -177,3 +177,9 @@ rust::String cOverloadedFunction(int32_t x); rust::String cOverloadedFunction(rust::Str x); } // namespace tests + +namespace other { + void ns_c_take_trivial(::tests::D d); + ::tests::D ns_c_return_trivial(); + void ns_c_take_ns_shared(::A::AShared shared); +} // namespace other \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs index 39a700e..21f8a8c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -103,6 +103,7 @@ fn test_c_take() { check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 })); + check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 })); check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); @@ -226,6 +227,8 @@ fn test_extern_trivial() { let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); cxx::UniquePtr::new(ffi2::D { d: 42 }); + let d = ffi2::ns_c_return_trivial(); + check!(ffi2::ns_c_take_trivial(d)); let g = ffi2::c_return_trivial_ns(); check!(ffi2::c_take_trivial_ns_ref(&g)); From 0fac32193904fdd75dcff71e5e4bd846b6e560b7 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:17:02 +0000 Subject: [PATCH 1064/2232] Adding tests for method calls in foreign namespaces --- diff --git a/tests/BUCK b/tests/BUCK index 5bbe500..f51be09 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -15,6 +15,7 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", + "ffi/class_in_ns.rs", ], crate = "cxx_test_suite", deps = [ @@ -30,6 +31,7 @@ cxx_library( ":bridge/source", ":extra/source", ":module/source", + ":class_in_ns/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -52,3 +54,8 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) + +rust_cxx_bridge( + name = "class_in_ns", + src = "ffi/class_in_ns.rs", +) diff --git a/tests/BUILD b/tests/BUILD index 57ffab9..a400f47 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -18,6 +18,7 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", + "ffi/class_in_ns.rs", ], deps = [ ":impl", @@ -32,6 +33,7 @@ cc_library( ":bridge/source", ":extra/source", ":module/source", + ":class_in_ns/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -57,3 +59,9 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) + +rust_cxx_bridge( + name = "class_in_ns", + src = "ffi/class_in_ns.rs", + deps = [":impl"], +) \ No newline at end of file diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 4b2cbdf..9bdb711 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "extra.rs", "module.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs", "class_in_ns.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs new file mode 100644 index 0000000..8b50561 --- /dev/null +++ b/tests/ffi/class_in_ns.rs @@ -0,0 +1,21 @@ +// To test receivers on a type in a namespace outide +// the default. cxx::bridge blocks can only have a single +// receiver type, and there can only be one such block per, +// which is why this is outside. + +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi3 { + + extern "C" { + include!("tests/ffi/tests.h"); + + #[namespace (namespace = I)] + type I; + + fn get(self: &I) -> u32; + + #[namespace (namespace = I)] + fn ns_c_return_unique_ptr_ns() -> UniquePtr; + } +} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index a491ae2..be145f3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,6 +4,7 @@ clippy::trivially_copy_pass_by_ref )] +pub mod class_in_ns; pub mod extra; pub mod module; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 1f0f8ff..05bf5fb 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -647,4 +647,14 @@ namespace other { cxx_test_suite_set_correct(); } } -} \ No newline at end of file +} // namespace other + +namespace I { + uint32_t I::get() const { + return a; + } + + std::unique_ptr ns_c_return_unique_ptr_ns() { + return std::unique_ptr(new I()); + } +} // namespace I diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 40bc802..3835660 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -182,4 +182,16 @@ namespace other { void ns_c_take_trivial(::tests::D d); ::tests::D ns_c_return_trivial(); void ns_c_take_ns_shared(::A::AShared shared); -} // namespace other \ No newline at end of file +} // namespace other + +namespace I { + class I { + private: + uint32_t a; + public: + I() : a(1000) {} + uint32_t get() const; + }; + + std::unique_ptr ns_c_return_unique_ptr_ns(); +} // namespace I diff --git a/tests/test.rs b/tests/test.rs index 21f8a8c..20b23ac 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,3 +1,4 @@ +use cxx_test_suite::class_in_ns::ffi3; use cxx_test_suite::extra::ffi2; use cxx_test_suite::ffi; use std::cell::Cell; @@ -194,6 +195,14 @@ fn test_c_method_calls() { } #[test] +fn test_c_ns_method_calls() { + let unique_ptr = ffi3::ns_c_return_unique_ptr_ns(); + + let old_value = unique_ptr.get(); + assert_eq!(1000, old_value); +} + +#[test] fn test_enum_representations() { assert_eq!(0, ffi::Enum::AVal.repr); assert_eq!(2020, ffi::Enum::BVal.repr); From c871343ac270f073fd4790269da267e8cb91dbde Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 26 2020 22:23:34 +0000 Subject: [PATCH 1065/2232] Allow namespace override. This change allows a #[namespace (namespace = A::B)] attribute for each item in a cxx::bridge. We now have a fair number of different types of name floating around: * C++ identifiers * C++ fully-qualified names * Rust identifiers * Rust fully-qualified names (future, when we support sub-modules) * Items with both a Rust and C++ name (a 'Pair') * Types with only a known Rust name, which can be resolved to a C++ name. This change attempts to put some sensible names for all these things in syntax/mod.rs, and so that would be a good place to start review. At the moment, the Namespace (included in each CppName) is ruthlessly cloned all over the place. As a given namespace is likely to be applicable to many types and functions, it may save significant memory in future to use Rc<> here. But let's not optimise too early. --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2fee0e9..92e8ec8 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -5,6 +5,7 @@ pub(super) mod error; mod file; pub(super) mod fs; pub(super) mod include; +mod namespace_organizer; pub(super) mod out; mod write; @@ -109,22 +110,22 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted); + let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; // Some callers may wish to generate both header and C++ // from the same token stream to avoid parsing twice. But others // only need to generate one or the other. Ok(GeneratedCode { header: if opt.gen_header { - write::gen(namespace, apis, types, opt, true).content() + write::gen(apis, types, opt, true).content() } else { Vec::new() }, implementation: if opt.gen_implementation { - write::gen(namespace, apis, types, opt, false).content() + write::gen(apis, types, opt, false).content() } else { Vec::new() }, diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs new file mode 100644 index 0000000..c54e784 --- /dev/null +++ b/gen/src/namespace_organizer.rs @@ -0,0 +1,40 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::BTreeMap; + +pub(crate) struct NamespaceEntries<'a> { + pub(crate) entries: Vec<&'a Api>, + pub(crate) children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, +} + +pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { + let api_refs = apis.iter().collect::>(); + sort_by_inner_namespace(api_refs, 0) +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: BTreeMap::new(), + }; + + let mut kids_by_child_ns = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } + } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, sort_by_inner_namespace(v, depth + 1)); + } + + root +} diff --git a/gen/src/out.rs b/gen/src/out.rs index d42ea74..8a6bd86 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,10 +1,8 @@ use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { - pub namespace: Namespace, pub header: bool, pub include: Includes, pub front: Content, @@ -18,9 +16,8 @@ pub struct Content { } impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { + pub fn new(header: bool) -> Self { OutFile { - namespace, header, include: Includes::new(), front: Content::new(), diff --git a/gen/src/write.rs b/gen/src/write.rs index 3184604..2ff5b47 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,20 +1,17 @@ +use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type, + Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen( - namespace: &Namespace, - apis: &[Api], - types: &Types, - opt: &Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); +pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFile { + let mut out_file = OutFile::new(header); let out = &mut out_file; if header { @@ -32,16 +29,36 @@ pub(super) fn gen( write_include_cxxbridge(out, apis, types); out.next_section(); - for name in namespace { - writeln!(out, "namespace {} {{", name); + + let apis_by_namespace = sort_by_namespace(apis); + + gen_namespace_contents(&apis_by_namespace, types, opt, header, out); + + if !header { + out.next_section(); + write_generic_instantiations(out, types); } + write!(out.front, "{}", out.include); + + out_file +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = &ns_entries.entries; + out.next_section(); for api in apis { match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), _ => {} } } @@ -51,7 +68,7 @@ pub(super) fn gen( if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type - .entry(&receiver.ty) + .entry(&receiver.ty.rust) .or_insert_with(Vec::new) .push(efn); } @@ -62,22 +79,22 @@ pub(super) fn gen( match api { Api::Struct(strct) => { out.next_section(); - if !types.cxx.contains(&strct.ident) { - write_struct(out, strct); + if !types.cxx.contains(&strct.ident.rust) { + write_struct(out, strct, types); } } Api::Enum(enm) => { out.next_section(); - if types.cxx.contains(&enm.ident) { + if types.cxx.contains(&enm.ident.rust) { check_enum(out, enm); } else { write_enum(out, enm); } } Api::RustType(ety) => { - if let Some(methods) = methods_for_type.get(&ety.ident) { + if let Some(methods) = methods_for_type.get(&ety.ident.rust) { out.next_section(); - write_struct_with_methods(out, ety, methods); + write_struct_with_methods(out, ety, methods, types); } } _ => {} @@ -87,8 +104,8 @@ pub(super) fn gen( out.next_section(); for api in apis { if let Api::TypeAlias(ety) = api { - if types.required_trivial.contains_key(&ety.ident) { - check_trivial_extern_type(out, &ety.ident) + if types.required_trivial.contains_key(&ety.ident.rust) { + check_trivial_extern_type(out, &ety.ident.cxx) } } } @@ -116,24 +133,18 @@ pub(super) fn gen( } out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - if !header { - out.next_section(); - write_generic_instantiations(out, types); + for (child_ns, child_ns_entries) in &ns_entries.children { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_contents(&child_ns_entries, types, opt, header, out); + writeln!(out, "}} // namespace {}", child_ns); } - - write!(out.front, "{}", out.include); - - out_file } fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from(&ident.rust) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, @@ -332,17 +343,17 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, strct.ident); +fn write_struct(out: &mut OutFile, strct: &Struct, types: &Types) { + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident); + writeln!(out, "struct {} final {{", strct.ident.cxx.ident); for field in &strct.fields { write!(out, " "); - write_type_space(out, &field.ty); + write_type_space(out, &field.ty, types); writeln!(out, "{};", field.ident); } writeln!(out, "}};"); @@ -353,25 +364,39 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } -fn write_struct_using(out: &mut OutFile, ident: &Ident) { - writeln!(out, "using {} = {};", ident, ident); +fn write_struct_using(out: &mut OutFile, ident: &CppName) { + writeln!( + out, + "using {} = {};", + ident.ident, + ident.to_fully_qualified() + ); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, ety.ident); +fn write_struct_with_methods( + out: &mut OutFile, + ety: &ExternType, + methods: &[&ExternFn], + types: &Types, +) { + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + writeln!(out, "struct {} final {{", ety.ident.cxx.ident); + writeln!(out, " {}() = delete;", ety.ident.cxx.ident); + writeln!( + out, + " {}(const {} &) = delete;", + ety.ident.cxx.ident, ety.ident.cxx.ident + ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); + let local_name = method.ident.cxx.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, false, types); writeln!(out, ";"); } writeln!(out, "}};"); @@ -379,13 +404,13 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE05_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident); + write!(out, "enum class {} : ", enm.ident.cxx.ident); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -396,7 +421,11 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write!( + out, + "static_assert(sizeof({}) == sizeof(", + enm.ident.cxx.ident + ); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -405,12 +434,12 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, variant.discriminant, + enm.ident.cxx.ident, variant.ident, variant.discriminant, ); } } -fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { +fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { // NOTE: The following two static assertions are just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: @@ -429,6 +458,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { // not being recognized as such by the C++ type system due to a move // constructor or destructor. + let id = &id.to_fully_qualified(); out.include.type_traits = true; writeln!(out, "static_assert("); writeln!( @@ -450,7 +480,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { ); } -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { +fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { let mut has_cxx_throws = false; for api in apis { if let Api::CxxFunction(efn) = api { @@ -486,13 +516,17 @@ fn write_cxx_function_shim( } else { write_extern_return_type_space(out, &efn.ret, types); } - let mangled = mangle::extern_fn(&out.namespace, efn); + let mangled = mangle::extern_fn(efn, types); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!( + out, + "{} &self", + types.resolve(&receiver.ty).to_fully_qualified() + ); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -510,21 +544,26 @@ fn write_cxx_function_shim( if !efn.args.is_empty() || efn.receiver.is_some() { write!(out, ", "); } - write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap(), types); write!(out, "*return$"); } writeln!(out, ") noexcept {{"); write!(out, " "); - write_return_type(out, &efn.ret); + write_return_type(out, &efn.ret, types); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident.rust), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), + Some(receiver) => write!( + out, + "({}::*{}$)(", + types.resolve(&receiver.ty).to_fully_qualified(), + efn.ident.rust + ), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); } write!(out, ")"); if let Some(receiver) = &efn.receiver { @@ -534,8 +573,13 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.cxx), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), + None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()), + Some(receiver) => write!( + out, + "&{}::{}", + types.resolve(&receiver.ty).to_fully_qualified(), + efn.ident.cxx.ident + ), } writeln!(out, ";"); write!(out, " "); @@ -548,7 +592,7 @@ fn write_cxx_function_shim( if indirect_return { out.include.new = true; write!(out, "new (return$) "); - write_indirect_return_type(out, efn.ret.as_ref().unwrap()); + write_indirect_return_type(out, efn.ret.as_ref().unwrap(), types); write!(out, "("); } else if efn.ret.is_some() { write!(out, "return "); @@ -570,10 +614,10 @@ fn write_cxx_function_shim( write!(out, ", "); } if let Type::RustBox(_) = &arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "::from_raw({})", arg.ident); } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "({})", arg.ident); } else if arg.ty == RustString { write!( @@ -582,7 +626,7 @@ fn write_cxx_function_shim( arg.ident, ); } else if let Type::RustVec(_) = arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { out.include.utility = true; @@ -632,17 +676,17 @@ fn write_function_pointer_trampoline( types: &Types, ) { out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let r_trampoline = mangle::r_trampoline(efn, var, types); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + let c_trampoline = mangle::c_trampoline(efn, var, types).to_string(); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { - let link_name = mangle::extern_fn(&out.namespace, efn); + let link_name = mangle::extern_fn(efn, types); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -665,7 +709,11 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!( + out, + "{} &self", + types.resolve(&receiver.ty).to_fully_qualified() + ); needs_comma = true; } for arg in &sig.args { @@ -679,7 +727,7 @@ fn write_rust_function_decl_impl( if needs_comma { write!(out, ", "); } - write_return_type(out, &sig.ret); + write_return_type(out, &sig.ret, types); write!(out, "*return$"); needs_comma = true; } @@ -697,10 +745,14 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), + None => efn.ident.cxx.ident.to_string(), + Some(receiver) => format!( + "{}::{}", + types.resolve(&receiver.ty).ident, + efn.ident.cxx.ident + ), }; - let invoke = mangle::extern_fn(&out.namespace, efn); + let invoke = mangle::extern_fn(efn, types); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } @@ -710,14 +762,15 @@ fn write_rust_function_shim_decl( local_name: &str, sig: &Signature, indirect_call: bool, + types: &Types, ) { - write_return_type(out, &sig.ret); + write_return_type(out, &sig.ret, types); write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } - write_type_space(out, &arg.ty); + write_type_space(out, &arg.ty, types); write!(out, "{}", arg.ident); } if indirect_call { @@ -749,7 +802,7 @@ fn write_rust_function_shim_impl( // We've already defined this inside the struct. return; } - write_rust_function_shim_decl(out, local_name, sig, indirect_call); + write_rust_function_shim_decl(out, local_name, sig, indirect_call, types); if out.header { writeln!(out, ";"); return; @@ -759,7 +812,7 @@ fn write_rust_function_shim_impl( if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); writeln!(out, "> {}$(::std::move({0}));", arg.ident); } } @@ -767,18 +820,18 @@ fn write_rust_function_shim_impl( let indirect_return = indirect_return(sig, types); if indirect_return { write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap()); + write_type(out, sig.ret.as_ref().unwrap(), types); writeln!(out, "> return$;"); write!(out, " "); } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { - write_type(out, ret); + write_type(out, ret, types); write!(out, "::from_raw("); } Type::UniquePtr(_) => { - write_type(out, ret); + write_type(out, ret, types); write!(out, "("); } Type::Ref(_) => write!(out, "*"), @@ -844,10 +897,10 @@ fn write_rust_function_shim_impl( writeln!(out, "}}"); } -fn write_return_type(out: &mut OutFile, ty: &Option) { +fn write_return_type(out: &mut OutFile, ty: &Option, types: &Types) { match ty { None => write!(out, "void "), - Some(ty) => write_type_space(out, ty), + Some(ty) => write_type_space(out, ty, types), } } @@ -857,27 +910,27 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } -fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { +fn write_indirect_return_type(out: &mut OutFile, ty: &Type, types: &Types) { match ty { Type::RustBox(ty) | Type::UniquePtr(ty) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Type::Ref(ty) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), - _ => write_type(out, ty), + _ => write_type(out, ty, types), } } -fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { - write_indirect_return_type(out, ty); +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type, types: &Types) { + write_indirect_return_type(out, ty, types); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), @@ -888,32 +941,32 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { match ty { Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Some(Type::Ref(ty)) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), - _ => write_return_type(out, ty), + _ => write_return_type(out, ty, types), } } fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), - _ => write_type_space(out, &arg.ty), + _ => write_type_space(out, &arg.ty, types), } if types.needs_indirect_abi(&arg.ty) { write!(out, "*"); @@ -921,37 +974,37 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write!(out, "{}", arg.ident); } -fn write_type(out: &mut OutFile, ty: &Type) { +fn write_type(out: &mut OutFile, ty: &Type, types: &Types) { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from(&ident.rust) { Some(atom) => write_atom(out, atom), - None => write!(out, "{}", ident), + None => write!(out, "{}", types.resolve(ident).to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::RustVec(ty) => { write!(out, "::rust::Vec<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); + write_type(out, &ptr.inner, types); write!(out, ">"); } Type::CxxVector(ty) => { write!(out, "::std::vector<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::Ref(r) => { if r.mutability.is_none() { write!(out, "const "); } - write_type(out, &r.inner); + write_type(out, &r.inner, types); write!(out, " &"); } Type::Slice(_) => { @@ -967,7 +1020,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { - Some(ret) => write_type(out, ret), + Some(ret) => write_type(out, ret, types), None => write!(out, "void"), } write!(out, "("); @@ -975,7 +1028,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); } write!(out, ")>"); } @@ -1003,8 +1056,8 @@ fn write_atom(out: &mut OutFile, atom: Atom) { } } -fn write_type_space(out: &mut OutFile, ty: &Type) { - write_type(out, ty); +fn write_type_space(out: &mut OutFile, ty: &Type, types: &Types) { + write_type(out, ty, types); write_space_after_type(out, ty); } @@ -1025,28 +1078,20 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { +fn to_typename(ty: &Type, types: &Types) -> String { match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + Type::Ident(ident) => types.resolve(&ident).to_fully_qualified(), + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner, types)), _ => unreachable!(), } } // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { +fn to_mangled(ty: &Type, types: &Types) -> Symbol { match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_symbol(types), + Type::CxxVector(ptr) => to_mangled(&ptr.inner, types).prefix_with("std$vector$"), _ => unreachable!(), } } @@ -1057,19 +1102,20 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { out.next_section(); - write_rust_box_extern(out, inner); + write_rust_box_extern(out, &types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(&inner.rust).is_none() { out.next_section(); - write_rust_vec_extern(out, inner); + write_rust_vec_extern(out, inner, types); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() - && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + if Atom::from(&inner.rust).is_none() + && (!types.aliases.contains_key(&inner.rust) + || types.explicit_impls.contains(ty)) { out.next_section(); write_unique_ptr(out, inner, types); @@ -1077,8 +1123,9 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() - && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + if Atom::from(&inner.rust).is_none() + && (!types.aliases.contains_key(&inner.rust) + || types.explicit_impls.contains(ty)) { out.next_section(); write_cxx_vector(out, ty, inner, types); @@ -1093,12 +1140,12 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, inner); + write_rust_box_impl(out, &types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - write_rust_vec_impl(out, inner); + if Atom::from(&inner.rust).is_none() { + write_rust_vec_impl(out, inner, types); } } } @@ -1107,14 +1154,9 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("namespace rust"); } -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); @@ -1131,10 +1173,10 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); @@ -1166,14 +1208,9 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_impl(out: &mut OutFile, ident: &CppName) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); @@ -1186,10 +1223,10 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); @@ -1225,9 +1262,9 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName, types: &Types) { let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); + let instance = to_mangled(&ty, types); writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); @@ -1241,8 +1278,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { out.include.new = true; out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); + let inner = to_typename(ty, types); + let instance = to_mangled(ty, types); let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. We can't @@ -1250,7 +1287,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { // bindings for a "new" method anyway. But the Rust code can't be called // for Opaque types because the 'new' method is not implemented. Type::Ident(ident) => { - types.structs.contains_key(ident) || types.aliases.contains_key(ident) + types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) } _ => false, }; @@ -1315,10 +1352,10 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4a4ec64..1bcaa1e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,12 +1,11 @@ use crate::derive::DeriveAttribute; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::file::Module; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, - Types, + self, check, mangle, Api, CppName, Enum, ExternFn, ExternType, Impl, ResolvableName, Signature, + Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -17,11 +16,11 @@ pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, content, trusted); + let namespace = &ffi.namespace; + let ref apis = syntax::parse_items(errors, content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - let namespace = &ffi.namespace; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; Ok(expand(ffi, apis, types)) @@ -30,7 +29,6 @@ pub fn bridge(mut ffi: Module) -> Result { fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { @@ -42,23 +40,23 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), - Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), + Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { let ident = &ety.ident; - if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { - expanded.extend(expand_cxx_type(namespace, ety)); + if !types.structs.contains_key(&ident.rust) + && !types.enums.contains_key(&ident.rust) + { + expanded.extend(expand_cxx_type(ety)); } } Api::CxxFunction(efn) => { - expanded.extend(expand_cxx_function_shim(namespace, efn, types)); - } - Api::RustFunction(efn) => { - hidden.extend(expand_rust_function_shim(namespace, efn, types)) + expanded.extend(expand_cxx_function_shim(efn, types)); } + Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias, types)); + hidden.extend(expand_type_alias_verify(alias, types)); } } } @@ -67,33 +65,33 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_box(namespace, ident)); + if Atom::from(&ident.rust).is_none() { + hidden.extend(expand_rust_box(ident, types)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_vec(namespace, ident)); + if Atom::from(&ident.rust).is_none() { + hidden.extend(expand_rust_vec(ident, types)); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() - && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) + if Atom::from(&ident.rust).is_none() + && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust)) { - expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); + expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() - && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) + if Atom::from(&ident.rust).is_none() + && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust)) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); + expanded.extend(expand_cxx_vector(ident, explicit_impl, types)); } } } @@ -126,11 +124,12 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { - let ident = &strct.ident; +fn expand_struct(strct: &Struct) -> TokenStream { + let ident = &strct.ident.rust; + let cxx_ident = &strct.ident.cxx; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(namespace, ident); + let type_id = type_id(cxx_ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -153,11 +152,12 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { } } -fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { - let ident = &enm.ident; +fn expand_enum(enm: &Enum) -> TokenStream { + let ident = &enm.ident.rust; + let cxx_ident = &enm.ident.cxx; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(namespace, ident); + let type_id = type_id(cxx_ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -186,10 +186,11 @@ fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { - let ident = &ety.ident; +fn expand_cxx_type(ety: &ExternType) -> TokenStream { + let ident = &ety.ident.rust; + let cxx_ident = &ety.ident.cxx; let doc = &ety.doc; - let type_id = type_id(namespace, ident); + let type_id = type_id(&cxx_ident); quote! { #doc @@ -205,7 +206,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } } -fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -236,7 +237,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = mangle::extern_fn(namespace, efn); + let link_name = mangle::extern_fn(efn, types); let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] @@ -244,9 +245,9 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } } -fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; - let decl = expand_cxx_function_decl(namespace, efn, types); + let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver.iter().map(|receiver| { let ampersand = receiver.ampersand; let mutability = receiver.mutability; @@ -272,14 +273,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let arg_vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString) } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => quote!(::cxx::private::RustString::from_ref(#var)), Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), }, @@ -306,9 +307,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .filter_map(|arg| { if let Type::Fn(f) = &arg.ty { let var = &arg.ident; - Some(expand_function_pointer_trampoline( - namespace, efn, var, f, types, - )) + Some(expand_function_pointer_trampoline(efn, var, f, types)) } else { None } @@ -355,7 +354,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }; let expr = if efn.throws { efn.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), @@ -368,7 +367,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_string()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), }, @@ -388,7 +387,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } else { efn.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), + Type::Ident(ident) if ident.rust == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), Type::RustVec(vec) => { if vec.inner == RustString { @@ -399,7 +398,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(#call.as_string())), Some(_) => Some(quote!(#call.as_mut_string())), }, @@ -445,14 +444,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_function_pointer_trampoline( - namespace: &Namespace, efn: &ExternFn, var: &Ident, sig: &Signature, types: &Types, ) -> TokenStream { - let c_trampoline = mangle::c_trampoline(namespace, efn, var); - let r_trampoline = mangle::r_trampoline(namespace, efn, var); + let c_trampoline = mangle::c_trampoline(efn, var, types); + let r_trampoline = mangle::r_trampoline(efn, var, types); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( @@ -500,7 +498,7 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { let sized = quote_spanned! {ety.semi_token.span=> #begin_span std::marker::Sized }; - quote_spanned! {ident.span()=> + quote_spanned! {ident.rust.span()=> let _ = { fn __AssertSized() {} __AssertSized::<#ident> @@ -508,8 +506,8 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } } -fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let link_name = mangle::extern_fn(namespace, efn); +fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { + let link_name = mangle::extern_fn(efn, types); let local_name = format_ident!("__{}", efn.ident.rust); let catch_unwind_label = format!("::{}", efn.ident.rust); let invoke = Some(&efn.ident.rust); @@ -553,7 +551,7 @@ fn expand_rust_function_shim_impl( let arg_vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { - Type::Ident(i) if i == RustString => { + Type::Ident(i) if i.rust == RustString => { quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), @@ -566,7 +564,7 @@ fn expand_rust_function_shim_impl( } Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(i) if i == RustString => match ty.mutability { + Type::Ident(i) if i.rust == RustString => match ty.mutability { None => quote!(#ident.as_string()), Some(_) => quote!(#ident.as_mut_string()), }, @@ -601,7 +599,9 @@ fn expand_rust_function_shim_impl( call.extend(quote! { (#(#vars),*) }); let conversion = sig.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => Some(quote!(::cxx::private::RustString::from)), + Type::Ident(ident) if ident.rust == RustString => { + Some(quote!(::cxx::private::RustString::from)) + } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)), Type::RustVec(vec) => { if vec.inner == RustString { @@ -612,7 +612,7 @@ fn expand_rust_function_shim_impl( } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(::cxx::private::RustString::from_ref)), Some(_) => Some(quote!(::cxx::private::RustString::from_mut)), }, @@ -686,13 +686,9 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify( - namespace: &Namespace, - alias: &TypeAlias, - types: &Types, -) -> TokenStream { +fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.ident; - let type_id = type_id(namespace, ident); + let type_id = type_id(&ident.cxx); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); @@ -702,7 +698,7 @@ fn expand_type_alias_verify( const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial.contains_key(&alias.ident) { + if types.required_trivial.contains_key(&alias.ident.rust) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; @@ -712,25 +708,19 @@ fn expand_type_alias_verify( verify } -fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - +fn type_id(ident: &CppName) -> TokenStream { + let path = ident.to_fully_qualified(); quote! { ::cxx::type_id!(#path) } } -fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); +fn expand_rust_box(ident: &ResolvableName, types: &Types) -> TokenStream { + let link_prefix = format!("cxxbridge05$box${}$", types.resolve(ident).to_symbol()); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); - let local_prefix = format_ident!("{}__box_", ident); + let local_prefix = format_ident!("{}__box_", &ident.rust); let local_uninit = format_ident!("{}uninit", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); @@ -754,15 +744,15 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); +fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream { + let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types)); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_data = format!("{}data", link_prefix); let link_stride = format!("{}stride", link_prefix); - let local_prefix = format_ident!("{}__vec_", elem); + let local_prefix = format_ident!("{}__vec_", elem.rust); let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); @@ -800,13 +790,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { } fn expand_unique_ptr( - namespace: &Namespace, - ident: &Ident, + ident: &ResolvableName, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge05$unique_ptr${}{}$", namespace, ident); + let name = ident.rust.to_string(); + let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types)); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -814,21 +803,22 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { - Some(quote! { - fn __new(mut value: Self) -> *mut ::std::ffi::c_void { - extern "C" { - #[link_name = #link_new] - fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + let new_method = + if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) { + Some(quote! { + fn __new(mut value: Self) -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_new] + fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __new(&mut repr, &mut value) } + repr } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); - unsafe { __new(&mut repr, &mut value) } - repr - } - }) - } else { - None - }; + }) + } else { + None + }; let begin_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); @@ -883,16 +873,19 @@ fn expand_unique_ptr( } fn expand_cxx_vector( - namespace: &Namespace, - elem: &Ident, + elem: &ResolvableName, explicit_impl: Option<&Impl>, + types: &Types, ) -> TokenStream { let _ = explicit_impl; - let name = elem.to_string(); - let prefix = format!("cxxbridge05$std$vector${}{}$", namespace, elem); + let name = elem.rust.to_string(); + let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol(types)); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!( + "cxxbridge05$unique_ptr$std$vector${}$", + elem.to_symbol(types) + ); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); @@ -979,7 +972,7 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { fn expand_extern_type(ty: &Type) -> TokenStream { match ty { - Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString), + Type::Ident(ident) if ident.rust == RustString => quote!(::cxx::private::RustString), Type::RustBox(ty) | Type::UniquePtr(ty) => { let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) @@ -991,7 +984,7 @@ fn expand_extern_type(ty: &Type) -> TokenStream { Type::Ref(ty) => { let mutability = ty.mutability; match &ty.inner { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { quote!(&#mutability ::cxx::private::RustString) } Type::RustVec(ty) => { diff --git a/syntax/atom.rs b/syntax/atom.rs index 6e5fa88..7d0ef6b 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -81,7 +81,7 @@ impl AsRef for Atom { impl PartialEq for Type { fn eq(&self, atom: &Atom) -> bool { match self { - Type::Ident(ident) => ident == atom, + Type::Ident(ident) => ident.rust == atom, _ => false, } } diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4c8a3e5..25af229 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,3 +1,4 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::Atom::{self, *}; use crate::syntax::{Derive, Doc}; @@ -12,19 +13,7 @@ pub struct Parser<'a> { pub repr: Option<&'a mut Option>, pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, -} - -pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { - let mut doc = Doc::new(); - parse( - cx, - attrs, - Parser { - doc: Some(&mut doc), - ..Parser::default() - }, - ); - doc + pub namespace: Option<&'a mut Namespace>, } pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { @@ -79,6 +68,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } 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) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -131,3 +130,10 @@ fn parse_function_alias_attribute(input: ParseStream) -> Result { input.parse() } } + +fn parse_namespace_attribute(input: ParseStream) -> Result { + let content; + syn::parenthesized!(content in input); + let namespace = content.parse::()?; + Ok(namespace) +} diff --git a/syntax/check.rs b/syntax/check.rs index ced1570..9aba9ac 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,4 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ @@ -11,15 +10,13 @@ use quote::{quote, ToTokens}; use std::fmt::Display; pub(crate) struct Check<'a> { - namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, } -pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { +pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) { do_typecheck(&mut Check { - namespace, apis, types, errors: cx, @@ -27,11 +24,11 @@ pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], ty } fn do_typecheck(cx: &mut Check) { - ident::check_all(cx, cx.namespace, cx.apis); + ident::check_all(cx, cx.apis); for ty in cx.types { match ty { - Type::Ident(ident) => check_type_ident(cx, ident), + Type::Ident(ident) => check_type_ident(cx, &ident.rust), 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), @@ -67,20 +64,20 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, &format!("unsupported type: {}", ident)); + cx.error(ident, &format!("unsupported type: {}", ident.to_string())); } } fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.cxx.contains(ident) - && !cx.types.structs.contains_key(ident) - && !cx.types.enums.contains_key(ident) + if cx.types.cxx.contains(&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).is_none() { + if Atom::from(&ident.rust).is_none() { return; } } @@ -90,15 +87,15 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { if let Type::Ident(ident) = &ty.inner { - if cx.types.cxx.contains(ident) - && !cx.types.structs.contains_key(ident) - && !cx.types.enums.contains_key(ident) + if cx.types.cxx.contains(&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) { + 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(RustString) => return, @@ -112,11 +109,11 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.rust.contains(ident) { + if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); } - match Atom::from(ident) { + match Atom::from(&ident.rust) { None | Some(CxxString) => return, _ => {} } @@ -129,14 +126,14 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.rust.contains(ident) { + if cx.types.rust.contains(&ident.rust) { cx.error( ptr, "C++ vector containing a Rust type is not supported yet", ); } - match Atom::from(ident) { + 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, @@ -170,15 +167,15 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { fn check_api_struct(cx: &mut Check, strct: &Struct) { let ident = &strct.ident; - check_reserved_name(cx, ident); + check_reserved_name(cx, &ident.rust); 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(ident) { - if let Some(ety) = cx.types.untrusted.get(ident) { + if cx.types.cxx.contains(&ident.rust) { + if let Some(ety) = cx.types.untrusted.get(&ident.rust) { let msg = "extern shared struct must be declared in an `unsafe extern` block"; cx.error(ety, msg); } @@ -200,7 +197,7 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } fn check_api_enum(cx: &mut Check, enm: &Enum) { - check_reserved_name(cx, &enm.ident); + check_reserved_name(cx, &enm.ident.rust); if enm.variants.is_empty() { let span = span_for_enum_error(enm); @@ -209,11 +206,13 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } fn check_api_type(cx: &mut Check, ety: &ExternType) { - check_reserved_name(cx, &ety.ident); + check_reserved_name(cx, &ety.ident.rust); - if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { + if let Some(reason) = cx.types.required_trivial.get(&ety.ident.rust) { let what = match reason { - TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::StructField(strct) => { + format!("a field of `{}`", strct.ident.cxx.to_fully_qualified()) + } TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; @@ -229,7 +228,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); - if receiver.ty == "Self" { + if receiver.ty.is_self() { let mutability = match receiver.mutability { Some(_) => "mut ", None => "", @@ -241,9 +240,9 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { mutability = mutability, ); cx.error(span, msg); - } else if !cx.types.structs.contains_key(&receiver.ty) - && !cx.types.cxx.contains(&receiver.ty) - && !cx.types.rust.contains(&receiver.ty) + } 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"); } @@ -290,7 +289,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { fn check_api_impl(cx: &mut Check, imp: &Impl) { if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(&inner.rust).is_none() { return; } } @@ -357,7 +356,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { - Type::Ident(ident) => ident, + Type::Ident(ident) => &ident.rust, Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; @@ -400,20 +399,20 @@ fn span_for_receiver_error(receiver: &Receiver) -> TokenStream { fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { - if cx.types.structs.contains_key(ident) { + if cx.types.structs.contains_key(&ident.rust) { "struct".to_owned() - } else if cx.types.enums.contains_key(ident) { + } else if cx.types.enums.contains_key(&ident.rust) { "enum".to_owned() - } else if cx.types.aliases.contains_key(ident) { + } else if cx.types.aliases.contains_key(&ident.rust) { "C++ type".to_owned() - } else if cx.types.cxx.contains(ident) { + } else if cx.types.cxx.contains(&ident.rust) { "opaque C++ type".to_owned() - } else if cx.types.rust.contains(ident) { + } else if cx.types.rust.contains(&ident.rust) { "opaque Rust type".to_owned() - } else if Atom::from(ident) == Some(CxxString) { + } else if Atom::from(&ident.rust) == Some(CxxString) { "C++ string".to_owned() } else { - ident.to_string() + ident.rust.to_string() } } Type::RustBox(_) => "Box".to_owned(), diff --git a/syntax/ident.rs b/syntax/ident.rs index 66f7365..354790a 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,6 +1,5 @@ use crate::syntax::check::Check; -use crate::syntax::namespace::Namespace; -use crate::syntax::{error, Api}; +use crate::syntax::{error, Api, CppName}; use proc_macro2::Ident; fn check(cx: &mut Check, ident: &Ident) { @@ -13,28 +12,31 @@ fn check(cx: &mut Check, ident: &Ident) { } } -pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { - for segment in namespace { +fn check_ident(cx: &mut Check, ident: &CppName) { + for segment in &ident.ns { check(cx, segment); } + check(cx, &ident.ident); +} +pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check(cx, &strct.ident); + check_ident(cx, &strct.ident.cxx); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check(cx, &enm.ident); + check_ident(cx, &enm.ident.cxx); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check(cx, &ety.ident); + check_ident(cx, &ety.ident.cxx); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.ident.rust); @@ -43,7 +45,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { } } Api::TypeAlias(alias) => { - check(cx, &alias.ident); + check_ident(cx, &alias.ident.cxx); } } } diff --git a/syntax/impls.rs b/syntax/impls.rs index 6a177d5..a83ce74 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,8 +1,13 @@ -use crate::syntax::{ExternFn, Impl, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ + Api, CppName, ExternFn, Impl, Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, + Symbol, Ty1, Type, Types, +}; +use proc_macro2::{Ident, Span}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; +use syn::Token; impl Deref for ExternFn { type Target = Signature; @@ -274,3 +279,84 @@ impl Borrow for &Impl { &self.ty } } + +impl Pair { + /// Use this constructor when the item can't have a different + /// name in Rust and C++. For cases where #[rust_name] and similar + /// attributes can be used, construct the object by hand. + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { + rust: ident.clone(), + cxx: CppName::new(ns, ident), + } + } +} + +impl ResolvableName { + pub fn new(ident: Ident) -> Self { + Self { rust: ident } + } + + pub fn from_pair(pair: Pair) -> Self { + Self { rust: pair.rust } + } + + pub fn make_self(span: Span) -> Self { + Self { + rust: Token![Self](span).into(), + } + } + + pub fn is_self(&self) -> bool { + self.rust == "Self" + } + + pub fn span(&self) -> Span { + self.rust.span() + } + + pub fn to_symbol(&self, types: &Types) -> Symbol { + types.resolve(self).to_symbol() + } +} + +impl Api { + pub fn get_namespace(&self) -> Option<&Namespace> { + match self { + Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), + Api::CxxType(cty) => Some(&cty.ident.cxx.ns), + Api::Enum(enm) => Some(&enm.ident.cxx.ns), + Api::Struct(strct) => Some(&strct.ident.cxx.ns), + Api::RustType(rty) => Some(&rty.ident.cxx.ns), + Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), + Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, + } + } +} + +impl CppName { + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { ns, ident } + } + + fn iter_all_segments( + &self, + ) -> std::iter::Chain, std::iter::Once<&Ident>> { + self.ns.iter().chain(std::iter::once(&self.ident)) + } + + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } + + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) + } +} diff --git a/syntax/mangle.rs b/syntax/mangle.rs index e461887..9255feb 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,6 +1,5 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::{self, Symbol}; -use crate::syntax::ExternFn; +use crate::syntax::{ExternFn, Types}; use proc_macro2::Ident; const CXXBRIDGE: &str = "cxxbridge05"; @@ -11,19 +10,27 @@ macro_rules! join { }; } -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { +pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), - None => join!(namespace, CXXBRIDGE, efn.ident.rust), + Some(receiver) => { + let receiver_ident = types.resolve(&receiver.ty); + join!( + efn.ident.cxx.ns, + CXXBRIDGE, + receiver_ident.ident, + efn.ident.rust + ) + } + None => join!(efn.ident.cxx.ns, CXXBRIDGE, efn.ident.rust), } } // The C half of a function pointer trampoline. -pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 0) +pub fn c_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol { + join!(extern_fn(efn, types), var, 0) } // The Rust half of a function pointer trampoline. -pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 1) +pub fn r_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol { + join!(extern_fn(efn, types), var, 1) } diff --git a/syntax/mod.rs b/syntax/mod.rs index c8dea67..bb9566d 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -21,7 +21,9 @@ mod tokens; pub mod types; use self::discriminant::Discriminant; +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}; @@ -33,6 +35,29 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; +/// A Rust identifier will forver == a proc_macro2::Ident, +/// but for completeness here's a type alias. +pub type RsIdent = Ident; + +/// At the moment, a Rust name is simply a proc_macro2::Ident. +/// In the future, it may become namespaced based on a mod path. +pub type RsName = RsIdent; + +/// At the moment, a C++ identifier is also a proc_macro2::Ident. +/// In the future, we may wish to make a newtype wrapper here +/// to avoid confusion between C++ and Rust identifiers. +pub type CppIdent = Ident; + +#[derive(Clone)] +/// A C++ identifier in a particular namespace. +/// It is intentional that this does not impl Display, +/// because we want to force users actively to decide whether to output +/// it as a qualified name or as an unqualfiied name. +pub struct CppName { + pub ns: Namespace, + pub ident: CppIdent, +} + pub enum Api { Include(String), Struct(Struct), @@ -48,7 +73,7 @@ pub enum Api { pub struct ExternType { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: Pair, pub semi_token: Token![;], pub trusted: bool, } @@ -57,7 +82,7 @@ pub struct Struct { pub doc: Doc, pub derives: Vec, pub struct_token: Token![struct], - pub ident: Ident, + pub ident: Pair, pub brace_token: Brace, pub fields: Vec, } @@ -65,15 +90,18 @@ pub struct Struct { pub struct Enum { pub doc: Doc, pub enum_token: Token![enum], - pub ident: Ident, + pub ident: Pair, pub brace_token: Brace, pub variants: Vec, pub repr: Atom, } +/// A type with a defined Rust name and a fully resolved, +/// qualified, namespaced, C++ name. +#[derive(Clone)] pub struct Pair { - pub cxx: Ident, - pub rust: Ident, + pub cxx: CppName, + pub rust: RsName, } pub struct ExternFn { @@ -87,7 +115,7 @@ pub struct ExternFn { pub struct TypeAlias { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: Pair, pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], @@ -112,7 +140,7 @@ pub struct Signature { #[derive(Eq, PartialEq, Hash)] pub struct Var { - pub ident: Ident, + pub ident: RsIdent, // fields and variables are not namespaced pub ty: Type, } @@ -121,18 +149,18 @@ pub struct Receiver { pub lifetime: Option, pub mutability: Option, pub var: Token![self], - pub ty: Ident, + pub ty: ResolvableName, pub shorthand: bool, } pub struct Variant { - pub ident: Ident, + pub ident: RsIdent, pub discriminant: Discriminant, pub expr: Option, } pub enum Type { - Ident(Ident), + Ident(ResolvableName), RustBox(Box), RustVec(Box), UniquePtr(Box), @@ -146,7 +174,7 @@ pub enum Type { } pub struct Ty1 { - pub name: Ident, + pub name: ResolvableName, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], @@ -169,3 +197,10 @@ pub enum Lang { Cxx, Rust, } + +/// Wrapper for a type which needs to be resolved +/// before it can be printed in C++. +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct ResolvableName { + pub rust: RsName, +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 367ad05..0a86b6c 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, - Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, CppName, Doc, Enum, ExternFn, ExternType, Impl, Lang, Namespace, Pair, + Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -20,20 +20,22 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec { +pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namespace) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(cx, item) { + Item::Struct(item) => match parse_struct(cx, item, ns.clone()) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(cx, item) { + Item::Enum(item) => match parse_enum(cx, item, ns.clone()) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, - Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), - Item::Impl(item) => match parse_impl(item) { + Item::ForeignMod(foreign_mod) => { + parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, ns) + } + Item::Impl(item) => match parse_impl(item, ns) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, @@ -44,7 +46,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec apis } -fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -65,6 +67,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -81,7 +84,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { doc, derives, struct_token: item.struct_token, - ident: item.ident, + ident: Pair::new(ns.clone(), item.ident), brace_token: fields.brace_token, fields: fields .named @@ -89,14 +92,14 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { .map(|field| { Ok(Var { ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, + ty: parse_type(&field.ty, &ns)?, }) }) .collect::>()?, })) } -fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum, mut ns: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -117,6 +120,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { attrs::Parser { doc: Some(&mut doc), repr: Some(&mut repr), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -167,7 +171,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { Ok(Api::Enum(Enum { doc, enum_token, - ident: item.ident, + ident: Pair::new(ns, item.ident), brace_token, variants, repr, @@ -179,6 +183,7 @@ fn parse_foreign_mod( foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, + ns: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, @@ -202,11 +207,13 @@ fn parse_foreign_mod( let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted) { - Ok(ety) => items.push(ety), - Err(err) => cx.push(err), - }, - ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) { + ForeignItem::Type(foreign) => { + match parse_extern_type(cx, foreign, lang, trusted, ns.clone()) { + Ok(ety) => items.push(ety), + Err(err) => cx.push(err), + } + } + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, ns.clone()) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -216,10 +223,12 @@ fn parse_foreign_mod( Err(err) => cx.push(err), } } - ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) { - Ok(api) => items.push(api), - Err(err) => cx.push(err), - }, + ForeignItem::Verbatim(tokens) => { + match parse_extern_verbatim(cx, tokens, lang, ns.clone()) { + Ok(api) => items.push(api), + Err(err) => cx.push(err), + } + } _ => cx.error(foreign, "unsupported foreign item"), } } @@ -234,8 +243,8 @@ fn parse_foreign_mod( for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { - if receiver.ty == "Self" { - receiver.ty = single_type.clone(); + if receiver.ty.is_self() { + receiver.ty = ResolvableName::from_pair(single_type.clone()); } } } @@ -267,8 +276,18 @@ fn parse_extern_type( foreign_type: &ForeignItemType, lang: Lang, trusted: bool, + mut ns: Namespace, ) -> Result { - let doc = attrs::parse_doc(cx, &foreign_type.attrs); + let mut doc = Doc::new(); + attrs::parse( + cx, + &foreign_type.attrs, + attrs::Parser { + doc: Some(&mut doc), + namespace: Some(&mut ns), + ..Default::default() + }, + ); let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); let semi_token = foreign_type.semi_token; @@ -279,13 +298,18 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident, + ident: Pair::new(ns, ident), semi_token, trusted, })) } -fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + cx: &mut Errors, + foreign_fn: &ForeignItemFn, + lang: Lang, + mut ns: Namespace, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -310,6 +334,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R doc: Some(&mut doc), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -326,7 +351,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R lifetime: lifetime.clone(), mutability: arg.mutability, var: arg.self_token, - ty: Token![Self](arg.self_token.span).into(), + ty: ResolvableName::make_self(arg.self_token.span), shorthand: true, }); continue; @@ -341,7 +366,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, &ns)?; if ident != "self" { args.push_value(Var { ident, ty }); if let Some(comma) = comma { @@ -355,7 +380,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R ampersand: reference.ampersand, lifetime: reference.lifetime, mutability: reference.mutability, - var: Token![self](ident.span()), + var: Token![self](ident.rust.span()), ty: ident, shorthand: false, }); @@ -368,12 +393,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } let mut throws_tokens = None; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &ns)?; let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = Pair { - cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + cxx: CppName::new(ns, cxx_name.unwrap_or(foreign_fn.sig.ident.clone())), rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), }; let paren_token = foreign_fn.sig.paren_token; @@ -401,7 +426,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R })) } -fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { +fn parse_extern_verbatim( + cx: &mut Errors, + tokens: &TokenStream, + lang: Lang, + mut ns: Namespace, +) -> Result { // type Alias = crate::path::to::Type; let parse = |input: ParseStream| -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -416,12 +446,21 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - let doc = attrs::parse_doc(cx, &attrs); + let mut doc = Doc::new(); + attrs::parse( + cx, + &attrs, + attrs::Parser { + doc: Some(&mut doc), + namespace: Some(&mut ns), + ..Default::default() + }, + ); Ok(TypeAlias { doc, type_token, - ident, + ident: Pair::new(ns, ident), eq_token, ty, semi_token, @@ -440,7 +479,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } -fn parse_impl(imp: ItemImpl) -> Result { +fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span); @@ -466,7 +505,7 @@ fn parse_impl(imp: ItemImpl) -> Result { Ok(Api::Impl(Impl { impl_token: imp.impl_token, - ty: parse_type(&self_ty)?, + ty: parse_type(&self_ty, ns)?, brace_token: imp.brace_token, })) } @@ -503,21 +542,21 @@ fn parse_include(input: ParseStream) -> Result { Err(input.error("expected \"quoted/path/to\" or ")) } -fn parse_type(ty: &RustType) -> Result { +fn parse_type(ty: &RustType, ns: &Namespace) -> Result { match ty { - RustType::Reference(ty) => parse_type_reference(ty), - RustType::Path(ty) => parse_type_path(ty), - RustType::Slice(ty) => parse_type_slice(ty), - RustType::BareFn(ty) => parse_type_fn(ty), + RustType::Reference(ty) => parse_type_reference(ty, ns), + RustType::Path(ty) => parse_type_path(ty, ns), + RustType::Slice(ty) => parse_type_slice(ty, ns), + RustType::BareFn(ty) => parse_type_fn(ty, ns), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_reference(ty: &TypeReference) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; let which = match &inner { - Type::Ident(ident) if ident == "str" => { + Type::Ident(ident) if ident.rust == "str" => { if ty.mutability.is_some() { return Err(Error::new_spanned(ty, "unsupported type")); } else { @@ -525,7 +564,7 @@ fn parse_type_reference(ty: &TypeReference) -> Result { } } Type::Slice(slice) => match &slice.inner { - Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8, + Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8, _ => Type::Ref, }, _ => Type::Ref, @@ -538,19 +577,20 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }))) } -fn parse_type_path(ty: &TypePath) -> Result { +fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); + let maybe_resolved_ident = ResolvableName::new(ident.clone()); match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(ident)), + PathArguments::None => return Ok(Type::Ident(maybe_resolved_ident)), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::UniquePtr(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -558,9 +598,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::CxxVector(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -568,9 +608,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustBox(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -578,9 +618,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustVec(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -594,15 +634,15 @@ fn parse_type_path(ty: &TypePath) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } -fn parse_type_slice(ty: &TypeSlice) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_slice(ty: &TypeSlice, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; Ok(Type::Slice(Box::new(Slice { bracket: ty.bracket_token, inner, }))) } -fn parse_type_fn(ty: &TypeBareFn) -> Result { +fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, @@ -620,7 +660,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; let ident = match &arg.name { Some(ident) => ident.0.clone(), None => format_ident!("_{}", i), @@ -629,7 +669,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { }) .collect::>()?; let mut throws_tokens = None; - let ret = parse_return_type(&ty.output, &mut throws_tokens)?; + let ret = parse_return_type(&ty.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { unsafety: ty.unsafety, @@ -646,6 +686,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, + ns: &Namespace, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), @@ -667,7 +708,7 @@ fn parse_return_type( } } } - match parse_type(ret)? { + match parse_type(ret, ns)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } diff --git a/syntax/qualified.rs b/syntax/qualified.rs index be9bceb..5eefb8d 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -10,6 +10,7 @@ impl QualifiedName { pub fn parse_unquoted(input: ParseStream) -> Result { let mut segments = Vec::new(); let mut trailing_punct = true; + input.parse::>()?; while trailing_punct && input.peek(Ident::peek_any) { let ident = Ident::parse_any(input)?; segments.push(ident); diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 1e5b513..0b79d5f 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,4 +1,5 @@ use crate::syntax::namespace::Namespace; +use crate::syntax::CppName; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -19,12 +20,6 @@ impl ToTokens for Symbol { } } -impl From<&Ident> for Symbol { - fn from(ident: &Ident) -> Self { - Symbol(ident.to_string()) - } -} - impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -34,18 +29,47 @@ impl Symbol { self.0.write_fmt(format_args!("{}", segment)).unwrap(); assert!(self.0.len() > len_before); } + + pub fn from_idents<'a, T: Iterator>(it: T) -> Self { + let mut symbol = Symbol(String::new()); + for segment in it { + segment.write(&mut symbol); + } + assert!(!symbol.0.is_empty()); + symbol + } + + /// For example, for taking a symbol and then making a new symbol + /// for a vec of that symbol. + pub fn prefix_with(&self, prefix: &str) -> Symbol { + Symbol(format!("{}{}", prefix, self.to_string())) + } +} + +pub trait Segment { + fn write(&self, symbol: &mut Symbol); } -pub trait Segment: Display { +impl Segment for str { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for usize { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Ident { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Symbol { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } - -impl Segment for str {} -impl Segment for usize {} -impl Segment for Ident {} -impl Segment for Symbol {} impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { @@ -55,9 +79,16 @@ impl Segment for Namespace { } } +impl Segment for CppName { + fn write(&self, symbol: &mut Symbol) { + self.ns.write(symbol); + self.ident.write(symbol); + } +} + impl Segment for &'_ T where - T: ?Sized + Segment, + T: ?Sized + Segment + Display, { fn write(&self, symbol: &mut Symbol) { (**self).write(symbol); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 7618e99..57db8eb 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, Signature, Slice, Struct, Ty1, - Type, TypeAlias, Var, + Atom, Derive, Enum, ExternFn, ExternType, Impl, Pair, Receiver, Ref, ResolvableName, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -11,11 +11,11 @@ impl ToTokens for Type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Type::Ident(ident) => { - if ident == CxxString { - let span = ident.span(); + if ident.rust == CxxString { + let span = ident.rust.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } - ident.to_tokens(tokens); + ident.rust.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => { ty.to_tokens(tokens) @@ -39,7 +39,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.name.span(); - let name = self.name.to_string(); + let name = self.name.rust.to_string(); if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); } else if name == "Vec" { @@ -121,6 +121,12 @@ impl ToTokens for ExternFn { } } +impl ToTokens for Pair { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.rust.to_tokens(tokens); + } +} + impl ToTokens for Impl { fn to_tokens(&self, tokens: &mut TokenStream) { self.impl_token.to_tokens(tokens); @@ -149,6 +155,12 @@ impl ToTokens for Signature { } } +impl ToTokens for ResolvableName { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.rust.to_tokens(tokens); + } +} + pub struct ReceiverType<'a>(&'a Receiver); impl Receiver { diff --git a/syntax/types.rs b/syntax/types.rs index 5bac76e..178da3e 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,10 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; +use crate::syntax::{ + Api, CppName, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type, + TypeAlias, +}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -16,6 +19,7 @@ pub struct Types<'a> { pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, pub explicit_impls: Set<&'a Impl>, + pub resolutions: Map<&'a Ident, &'a CppName>, } impl<'a> Types<'a> { @@ -28,6 +32,7 @@ impl<'a> Types<'a> { let mut aliases = Map::new(); let mut untrusted = Map::new(); let mut explicit_impls = Set::new(); + let mut resolutions = Map::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -50,6 +55,10 @@ impl<'a> Types<'a> { } } + let mut add_resolution = |pair: &'a Pair| { + resolutions.insert(&pair.rust, &pair.cxx); + }; + let mut type_names = UnorderedSet::new(); let mut function_names = UnorderedSet::new(); for api in apis { @@ -62,7 +71,7 @@ impl<'a> Types<'a> { match api { Api::Include(_) => {} Api::Struct(strct) => { - let ident = &strct.ident; + let ident = &strct.ident.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -73,13 +82,14 @@ impl<'a> Types<'a> { // type, then error. duplicate_name(cx, strct, ident); } - structs.insert(ident, strct); + structs.insert(&strct.ident.rust, strct); for field in &strct.fields { visit(&mut all, &field.ty); } + add_resolution(&strct.ident); } Api::Enum(enm) => { - let ident = &enm.ident; + let ident = &enm.ident.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -91,9 +101,10 @@ impl<'a> Types<'a> { duplicate_name(cx, enm, ident); } enums.insert(ident, enm); + add_resolution(&enm.ident); } Api::CxxType(ety) => { - let ident = &ety.ident; + let ident = &ety.ident.rust; if !type_names.insert(ident) && (cxx.contains(ident) || !structs.contains_key(ident) && !enums.contains_key(ident)) @@ -107,13 +118,15 @@ impl<'a> Types<'a> { if !ety.trusted { untrusted.insert(ident, ety); } + add_resolution(&ety.ident); } Api::RustType(ety) => { - let ident = &ety.ident; + let ident = &ety.ident.rust; if !type_names.insert(ident) { duplicate_name(cx, ety, ident); } rust.insert(ident); + add_resolution(&ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { // Note: duplication of the C++ name is fine because C++ has @@ -130,11 +143,12 @@ impl<'a> Types<'a> { } Api::TypeAlias(alias) => { let ident = &alias.ident; - if !type_names.insert(ident) { - duplicate_name(cx, alias, ident); + if !type_names.insert(&ident.rust) { + duplicate_name(cx, alias, &ident.rust); } - cxx.insert(ident); - aliases.insert(ident, alias); + cxx.insert(&ident.rust); + aliases.insert(&ident.rust, alias); + add_resolution(&alias.ident); } Api::Impl(imp) => { visit(&mut all, &imp.ty); @@ -150,8 +164,8 @@ impl<'a> Types<'a> { let mut required_trivial = Map::new(); let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| { if let Type::Ident(ident) = ty { - if cxx.contains(ident) { - required_trivial.entry(ident).or_insert(reason); + if cxx.contains(&ident.rust) { + required_trivial.entry(&ident.rust).or_insert(reason); } } }; @@ -187,16 +201,17 @@ impl<'a> Types<'a> { untrusted, required_trivial, explicit_impls, + resolutions, } } pub fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { Type::Ident(ident) => { - if let Some(strct) = self.structs.get(ident) { + if let Some(strct) = self.structs.get(&ident.rust) { !self.is_pod(strct) } else { - Atom::from(ident) == Some(RustString) + Atom::from(&ident.rust) == Some(RustString) } } Type::RustVec(_) => true, @@ -212,6 +227,12 @@ impl<'a> Types<'a> { } false } + + pub fn resolve(&self, ident: &ResolvableName) -> &CppName { + self.resolutions + .get(&ident.rust) + .expect("Unable to resolve type") + } } impl<'t, 'a> IntoIterator for &'t Types<'a> { diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 0a56dd4..a860f3d 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `S` +error: needs a cxx::ExternType impl in order to be used as a field of `::S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C; From b0cd3270b65ce4531c9a50d3b229d558fa1dffa3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 19:36:02 +0000 Subject: [PATCH 1066/2232] Track span information of include statements --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3184604..a354ae4 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,9 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, Enum, ExternFn, ExternType, IncludeKind, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; @@ -24,7 +26,10 @@ pub(super) fn gen( out.include.extend(opt.include.clone()); for api in apis { if let Api::Include(include) = api { - out.include.insert(include); + match include.kind { + IncludeKind::Quoted => out.include.insert(&include.path), + IncludeKind::Bracketed => out.include.insert(format!("<{}>", include.path)), + } } } diff --git a/syntax/impls.rs b/syntax/impls.rs index 6a177d5..a4b393a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,9 +1,27 @@ -use crate::syntax::{ExternFn, Impl, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; +impl PartialEq for Include { + fn eq(&self, other: &Include) -> bool { + let Include { + path, + kind, + begin_span: _, + end_span: _, + } = self; + let Include { + path: path2, + kind: kind2, + begin_span: _, + end_span: _, + } = other; + path == path2 && kind == kind2 + } +} + impl Deref for ExternFn { type Target = Signature; diff --git a/syntax/mod.rs b/syntax/mod.rs index c8dea67..2eccdb6 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -34,7 +34,7 @@ pub use self::parse::parse_items; pub use self::types::Types; pub enum Api { - Include(String), + Include(Include), Struct(Struct), Enum(Enum), CxxType(ExternType), @@ -45,6 +45,19 @@ pub enum Api { Impl(Impl), } +pub struct Include { + pub path: String, + pub kind: IncludeKind, + pub begin_span: Span, + pub end_span: Span, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum IncludeKind { + Quoted, // #include "quoted/path/to" + Bracketed, // #include +} + pub struct ExternType { pub doc: Doc, pub type_token: Token![type], diff --git a/syntax/parse.rs b/syntax/parse.rs index 367ad05..5b6abbb 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,10 +3,10 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, - Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Pair, + Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; @@ -471,17 +471,28 @@ fn parse_impl(imp: ItemImpl) -> Result { })) } -fn parse_include(input: ParseStream) -> Result { +fn parse_include(input: ParseStream) -> Result { if input.peek(LitStr) { - return Ok(input.parse::()?.value()); + let lit: LitStr = input.parse()?; + let span = lit.span(); + return Ok(Include { + path: lit.value(), + kind: IncludeKind::Quoted, + begin_span: span, + end_span: span, + }); } if input.peek(Token![<]) { let mut path = String::new(); + let mut begin_span = None; + let mut end_span = Span::call_site(); + input.parse::()?; - path.push('<'); while !input.is_empty() && !input.peek(Token![>]) { let token: TokenTree = input.parse()?; + end_span = token.span(); + begin_span = Some(begin_span.unwrap_or(end_span)); match token { TokenTree::Ident(token) => path += &token.to_string(), TokenTree::Literal(token) @@ -495,9 +506,16 @@ fn parse_include(input: ParseStream) -> Result { _ => return Err(Error::new(token.span(), "unexpected token in include path")), } } - input.parse::]>()?; - path.push('>'); - return Ok(path); + let rangle: Token![>] = input.parse()?; + let begin_span = + begin_span.ok_or_else(|| Error::new(rangle.span, "empty filename in #include"))?; + + return Ok(Include { + path, + kind: IncludeKind::Bracketed, + begin_span, + end_span, + }); } Err(input.error("expected \"quoted/path/to\" or ")) From d927633b8a24cc04a996b119a0c976ddb51f9851 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:22:51 +0000 Subject: [PATCH 1067/2232] Merge pull request #372 from dtolnay/include Track span information of include statements --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3184604..a354ae4 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -3,7 +3,9 @@ use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, Enum, ExternFn, ExternType, IncludeKind, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; @@ -24,7 +26,10 @@ pub(super) fn gen( out.include.extend(opt.include.clone()); for api in apis { if let Api::Include(include) = api { - out.include.insert(include); + match include.kind { + IncludeKind::Quoted => out.include.insert(&include.path), + IncludeKind::Bracketed => out.include.insert(format!("<{}>", include.path)), + } } } diff --git a/syntax/impls.rs b/syntax/impls.rs index 6a177d5..a4b393a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,9 +1,27 @@ -use crate::syntax::{ExternFn, Impl, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; +impl PartialEq for Include { + fn eq(&self, other: &Include) -> bool { + let Include { + path, + kind, + begin_span: _, + end_span: _, + } = self; + let Include { + path: path2, + kind: kind2, + begin_span: _, + end_span: _, + } = other; + path == path2 && kind == kind2 + } +} + impl Deref for ExternFn { type Target = Signature; diff --git a/syntax/mod.rs b/syntax/mod.rs index c8dea67..2eccdb6 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -34,7 +34,7 @@ pub use self::parse::parse_items; pub use self::types::Types; pub enum Api { - Include(String), + Include(Include), Struct(Struct), Enum(Enum), CxxType(ExternType), @@ -45,6 +45,19 @@ pub enum Api { Impl(Impl), } +pub struct Include { + pub path: String, + pub kind: IncludeKind, + pub begin_span: Span, + pub end_span: Span, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum IncludeKind { + Quoted, // #include "quoted/path/to" + Bracketed, // #include +} + pub struct ExternType { pub doc: Doc, pub type_token: Token![type], diff --git a/syntax/parse.rs b/syntax/parse.rs index 367ad05..5b6abbb 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,10 +3,10 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, - Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Pair, + Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; @@ -471,17 +471,28 @@ fn parse_impl(imp: ItemImpl) -> Result { })) } -fn parse_include(input: ParseStream) -> Result { +fn parse_include(input: ParseStream) -> Result { if input.peek(LitStr) { - return Ok(input.parse::()?.value()); + let lit: LitStr = input.parse()?; + let span = lit.span(); + return Ok(Include { + path: lit.value(), + kind: IncludeKind::Quoted, + begin_span: span, + end_span: span, + }); } if input.peek(Token![<]) { let mut path = String::new(); + let mut begin_span = None; + let mut end_span = Span::call_site(); + input.parse::()?; - path.push('<'); while !input.is_empty() && !input.peek(Token![>]) { let token: TokenTree = input.parse()?; + end_span = token.span(); + begin_span = Some(begin_span.unwrap_or(end_span)); match token { TokenTree::Ident(token) => path += &token.to_string(), TokenTree::Literal(token) @@ -495,9 +506,16 @@ fn parse_include(input: ParseStream) -> Result { _ => return Err(Error::new(token.span(), "unexpected token in include path")), } } - input.parse::]>()?; - path.push('>'); - return Ok(path); + let rangle: Token![>] = input.parse()?; + let begin_span = + begin_span.ok_or_else(|| Error::new(rangle.span, "empty filename in #include"))?; + + return Ok(Include { + path, + kind: IncludeKind::Bracketed, + begin_span, + end_span, + }); } Err(input.error("expected \"quoted/path/to\" or ")) From 4aae7c09b72bfb4299c44468f95bb6d140015900 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:23:21 +0000 Subject: [PATCH 1068/2232] Distinguish quoted vs bracketed includes in gen::include --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 3255902..959b029 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,4 +1,5 @@ use crate::gen::out::OutFile; +use crate::syntax::IncludeKind; use std::fmt::{self, Display}; /// The complete contents of the "rust/cxx.h" header. @@ -48,9 +49,15 @@ fn find_line(mut offset: usize, line: &str) -> Option { } } +#[derive(PartialEq)] +pub struct Include { + pub path: String, + pub kind: IncludeKind, +} + #[derive(Default, PartialEq)] pub struct Includes { - custom: Vec, + custom: Vec, pub array: bool, pub cstddef: bool, pub cstdint: bool, @@ -70,24 +77,30 @@ impl Includes { Includes::default() } - pub fn insert(&mut self, include: impl AsRef) { - self.custom.push(include.as_ref().to_owned()); + pub fn insert(&mut self, include: Include) { + self.custom.push(include); } } -impl Extend for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter); +impl<'a> Extend<&'a String> for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter.into_iter().map(|path| Include { + path: path.clone(), + kind: IncludeKind::Quoted, + })); } } impl Display for Includes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for include in &self.custom { - if include.starts_with('<') && include.ends_with('>') { - writeln!(f, "#include {}", include)?; - } else { - writeln!(f, "#include \"{}\"", include.escape_default())?; + match include.kind { + IncludeKind::Quoted => { + writeln!(f, "#include \"{}\"", include.path.escape_default())?; + } + IncludeKind::Bracketed => { + writeln!(f, "#include <{}>", include.path)?; + } } } if self.array { diff --git a/gen/src/write.rs b/gen/src/write.rs index a354ae4..f1c7a11 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,11 +1,10 @@ +use crate::gen::include::Include; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{ - mangle, Api, Enum, ExternFn, ExternType, IncludeKind, Signature, Struct, Type, Types, Var, -}; +use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -23,13 +22,12 @@ pub(super) fn gen( writeln!(out.front, "#pragma once"); } - out.include.extend(opt.include.clone()); + out.include.extend(&opt.include); for api in apis { if let Api::Include(include) = api { - match include.kind { - IncludeKind::Quoted => out.include.insert(&include.path), - IncludeKind::Bracketed => out.include.insert(format!("<{}>", include.path)), - } + let path = include.path.clone(); + let kind = include.kind; + out.include.insert(Include { path, kind }); } } From 36b1dbac2f746ab3a8a5344a836639dabd2eb818 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:33:33 +0000 Subject: [PATCH 1069/2232] Merge pull request #373 from dtolnay/include Distinguish quoted vs bracketed includes in gen::include --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 3255902..959b029 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,4 +1,5 @@ use crate::gen::out::OutFile; +use crate::syntax::IncludeKind; use std::fmt::{self, Display}; /// The complete contents of the "rust/cxx.h" header. @@ -48,9 +49,15 @@ fn find_line(mut offset: usize, line: &str) -> Option { } } +#[derive(PartialEq)] +pub struct Include { + pub path: String, + pub kind: IncludeKind, +} + #[derive(Default, PartialEq)] pub struct Includes { - custom: Vec, + custom: Vec, pub array: bool, pub cstddef: bool, pub cstdint: bool, @@ -70,24 +77,30 @@ impl Includes { Includes::default() } - pub fn insert(&mut self, include: impl AsRef) { - self.custom.push(include.as_ref().to_owned()); + pub fn insert(&mut self, include: Include) { + self.custom.push(include); } } -impl Extend for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter); +impl<'a> Extend<&'a String> for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter.into_iter().map(|path| Include { + path: path.clone(), + kind: IncludeKind::Quoted, + })); } } impl Display for Includes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for include in &self.custom { - if include.starts_with('<') && include.ends_with('>') { - writeln!(f, "#include {}", include)?; - } else { - writeln!(f, "#include \"{}\"", include.escape_default())?; + match include.kind { + IncludeKind::Quoted => { + writeln!(f, "#include \"{}\"", include.path.escape_default())?; + } + IncludeKind::Bracketed => { + writeln!(f, "#include <{}>", include.path)?; + } } } if self.array { diff --git a/gen/src/write.rs b/gen/src/write.rs index a354ae4..f1c7a11 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,11 +1,10 @@ +use crate::gen::include::Include; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{ - mangle, Api, Enum, ExternFn, ExternType, IncludeKind, Signature, Struct, Type, Types, Var, -}; +use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; use proc_macro2::Ident; use std::collections::HashMap; @@ -23,13 +22,12 @@ pub(super) fn gen( writeln!(out.front, "#pragma once"); } - out.include.extend(opt.include.clone()); + out.include.extend(&opt.include); for api in apis { if let Api::Include(include) = api { - match include.kind { - IncludeKind::Quoted => out.include.insert(&include.path), - IncludeKind::Bracketed => out.include.insert(format!("<{}>", include.path)), - } + let path = include.path.clone(); + let kind = include.kind; + out.include.insert(Include { path, kind }); } } From 700cd0c1df3ae50d125f366c57c5c21ccc604139 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:33:41 +0000 Subject: [PATCH 1070/2232] Expose IncludeKind to cxx_gen library --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index bd3bcfd..e2945a1 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -3,6 +3,8 @@ mod test; use super::{Opt, Output}; +use crate::gen::include::Include; +use crate::syntax::IncludeKind; use clap::AppSettings; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -63,7 +65,19 @@ pub(super) fn from_args() -> Opt { let include = matches .values_of(INCLUDE) .unwrap_or_default() - .map(str::to_owned) + .map(|include| { + if include.starts_with('<') && include.ends_with('>') { + Include { + path: include[1..include.len() - 1].to_owned(), + kind: IncludeKind::Bracketed, + } + } else { + Include { + path: include.to_owned(), + kind: IncludeKind::Quoted, + } + } + }) .collect(); let mut outputs = Vec::new(); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index f0fd9b4..cd58307 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -13,7 +13,8 @@ mod output; mod syntax; use crate::gen::error::{report, Result}; -use crate::gen::{fs, include}; +use crate::gen::fs; +use crate::gen::include::{self, Include}; use crate::output::Output; use std::io::{self, Write}; use std::path::PathBuf; @@ -24,7 +25,7 @@ struct Opt { input: Option, header: bool, cxx_impl_annotations: Option, - include: Vec, + include: Vec, outputs: Vec, } diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index ecfa436..963e870 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -20,8 +20,9 @@ mod gen; mod syntax; pub use crate::error::Error; -pub use crate::gen::include::HEADER; +pub use crate::gen::include::{Include, HEADER}; pub use crate::gen::{GeneratedCode, Opt}; +pub use crate::syntax::IncludeKind; use proc_macro2::TokenStream; /// Generate C++ bindings code from a Rust token stream. This should be a Rust diff --git a/gen/src/include.rs b/gen/src/include.rs index 959b029..705ca24 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -49,9 +49,16 @@ fn find_line(mut offset: usize, line: &str) -> Option { } } -#[derive(PartialEq)] +/// A header to #include. +/// +/// The cxxbridge tool does not parse or even require the given paths to exist; +/// they simply go into the generated C++ code as #include lines. +#[derive(Clone, PartialEq, Debug)] pub struct Include { + /// The header's path, not including the enclosing quotation marks or angle + /// brackets. pub path: String, + /// Whether to emit `#include "path"` or `#include `. pub kind: IncludeKind, } @@ -82,12 +89,9 @@ impl Includes { } } -impl<'a> Extend<&'a String> for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter.into_iter().map(|path| Include { - path: path.clone(), - kind: IncludeKind::Quoted, - })); +impl<'a> Extend<&'a Include> for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter.into_iter().cloned()); } } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2fee0e9..1a3febd 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -11,6 +11,7 @@ mod write; pub(super) use self::error::Error; use self::error::{format_err, Result}; use self::file::File; +use self::include::Include; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use std::path::Path; @@ -34,7 +35,7 @@ pub struct Opt { /// Any additional headers to #include. The cxxbridge tool does not parse or /// even require the given paths to exist; they simply go into the generated /// C++ code as #include lines. - pub include: Vec, + pub include: Vec, /// Optional annotation for implementations of C++ function wrappers that /// may be exposed to Rust. You may for example need to provide /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if diff --git a/syntax/mod.rs b/syntax/mod.rs index 2eccdb6..180c40a 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -52,10 +52,13 @@ pub struct Include { pub end_span: Span, } -#[derive(Copy, Clone, PartialEq)] +/// Whether to emit `#include "path"` or `#include `. +#[derive(Copy, Clone, PartialEq, Debug)] pub enum IncludeKind { - Quoted, // #include "quoted/path/to" - Bracketed, // #include + /// `#include "quoted/path/to"` + Quoted, + /// `#include ` + Bracketed, } pub struct ExternType { From d5107638a1a4ae0c84d5b9ca5a5e5101c65ff686 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:41:36 +0000 Subject: [PATCH 1071/2232] Merge pull request #374 from dtolnay/include Expose IncludeKind to cxx_gen library --- diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs index bd3bcfd..e2945a1 100644 --- a/gen/cmd/src/app.rs +++ b/gen/cmd/src/app.rs @@ -3,6 +3,8 @@ mod test; use super::{Opt, Output}; +use crate::gen::include::Include; +use crate::syntax::IncludeKind; use clap::AppSettings; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -63,7 +65,19 @@ pub(super) fn from_args() -> Opt { let include = matches .values_of(INCLUDE) .unwrap_or_default() - .map(str::to_owned) + .map(|include| { + if include.starts_with('<') && include.ends_with('>') { + Include { + path: include[1..include.len() - 1].to_owned(), + kind: IncludeKind::Bracketed, + } + } else { + Include { + path: include.to_owned(), + kind: IncludeKind::Quoted, + } + } + }) .collect(); let mut outputs = Vec::new(); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index f0fd9b4..cd58307 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -13,7 +13,8 @@ mod output; mod syntax; use crate::gen::error::{report, Result}; -use crate::gen::{fs, include}; +use crate::gen::fs; +use crate::gen::include::{self, Include}; use crate::output::Output; use std::io::{self, Write}; use std::path::PathBuf; @@ -24,7 +25,7 @@ struct Opt { input: Option, header: bool, cxx_impl_annotations: Option, - include: Vec, + include: Vec, outputs: Vec, } diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index ecfa436..963e870 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -20,8 +20,9 @@ mod gen; mod syntax; pub use crate::error::Error; -pub use crate::gen::include::HEADER; +pub use crate::gen::include::{Include, HEADER}; pub use crate::gen::{GeneratedCode, Opt}; +pub use crate::syntax::IncludeKind; use proc_macro2::TokenStream; /// Generate C++ bindings code from a Rust token stream. This should be a Rust diff --git a/gen/src/include.rs b/gen/src/include.rs index 959b029..705ca24 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -49,9 +49,16 @@ fn find_line(mut offset: usize, line: &str) -> Option { } } -#[derive(PartialEq)] +/// A header to #include. +/// +/// The cxxbridge tool does not parse or even require the given paths to exist; +/// they simply go into the generated C++ code as #include lines. +#[derive(Clone, PartialEq, Debug)] pub struct Include { + /// The header's path, not including the enclosing quotation marks or angle + /// brackets. pub path: String, + /// Whether to emit `#include "path"` or `#include `. pub kind: IncludeKind, } @@ -82,12 +89,9 @@ impl Includes { } } -impl<'a> Extend<&'a String> for Includes { - fn extend>(&mut self, iter: I) { - self.custom.extend(iter.into_iter().map(|path| Include { - path: path.clone(), - kind: IncludeKind::Quoted, - })); +impl<'a> Extend<&'a Include> for Includes { + fn extend>(&mut self, iter: I) { + self.custom.extend(iter.into_iter().cloned()); } } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2fee0e9..1a3febd 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -11,6 +11,7 @@ mod write; pub(super) use self::error::Error; use self::error::{format_err, Result}; use self::file::File; +use self::include::Include; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use std::path::Path; @@ -34,7 +35,7 @@ pub struct Opt { /// Any additional headers to #include. The cxxbridge tool does not parse or /// even require the given paths to exist; they simply go into the generated /// C++ code as #include lines. - pub include: Vec, + pub include: Vec, /// Optional annotation for implementations of C++ function wrappers that /// may be exposed to Rust. You may for example need to provide /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if diff --git a/syntax/mod.rs b/syntax/mod.rs index 2eccdb6..180c40a 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -52,10 +52,13 @@ pub struct Include { pub end_span: Span, } -#[derive(Copy, Clone, PartialEq)] +/// Whether to emit `#include "path"` or `#include `. +#[derive(Copy, Clone, PartialEq, Debug)] pub enum IncludeKind { - Quoted, // #include "quoted/path/to" - Bracketed, // #include + /// `#include "quoted/path/to"` + Quoted, + /// `#include ` + Bracketed, } pub struct ExternType { From 75c2385e3023e4cf4b0f18b09797be7304f0e4be Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:41:45 +0000 Subject: [PATCH 1072/2232] Check for disallowed include strings error[cxxbridge]: #include relative to `.` or `..` is not supported in Cargo builds ┌─ src/main.rs:10:18 │ 10 │ include!("../header.h"); │ ^^^^^^^^^^^^^ #include relative to `.` or `..` is not supported in Cargo builds │ = note: use a path starting with the crate name --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index e6de985..3e37143 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -271,7 +271,10 @@ fn make_include_dir(prj: &Project) -> Result { } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { - let opt = Opt::default(); + let opt = Opt { + allow_dot_includes: false, + ..Opt::default() + }; let generated = gen::generate_from_path(rust_source_file, &opt); let ref rel_path = paths::local_relative_path(rust_source_file); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index cd58307..c723039 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -66,6 +66,7 @@ fn try_main() -> Result<()> { cxx_impl_annotations: opt.cxx_impl_annotations, gen_header, gen_implementation, + ..Default::default() }; let generated_code = if let Some(input) = opt.input { diff --git a/gen/src/check.rs b/gen/src/check.rs new file mode 100644 index 0000000..35929ad --- /dev/null +++ b/gen/src/check.rs @@ -0,0 +1,27 @@ +use crate::gen::Opt; +use crate::syntax::report::Errors; +use crate::syntax::{error, Api}; +use quote::{quote, quote_spanned}; +use std::path::{Component, Path}; + +pub(super) use crate::syntax::check::typecheck; + +pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) { + if !opt.allow_dot_includes { + check_dot_includes(cx, apis); + } +} + +fn check_dot_includes(cx: &mut Errors, apis: &[Api]) { + for api in apis { + if let Api::Include(include) = api { + let first_component = Path::new(&include.path).components().next(); + if let Some(Component::CurDir) | Some(Component::ParentDir) = first_component { + let begin = quote_spanned!(include.begin_span=> .); + let end = quote_spanned!(include.end_span=> .); + let span = quote!(#begin #end); + cx.error(span, error::DOT_INCLUDE.msg); + } + } + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 1a3febd..236fece 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,6 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. +mod check; pub(super) mod error; mod file; pub(super) mod fs; @@ -13,7 +14,7 @@ use self::error::{format_err, Result}; use self::file::File; use self::include::Include; use crate::syntax::report::Errors; -use crate::syntax::{self, check, Types}; +use crate::syntax::{self, Types}; use std::path::Path; /// Options for C++ code generation. @@ -45,6 +46,7 @@ pub struct Opt { pub(super) gen_header: bool, pub(super) gen_implementation: bool, + pub(super) allow_dot_includes: bool, } /// Results of code generation. @@ -63,6 +65,7 @@ impl Default for Opt { cxx_impl_annotations: None, gen_header: true, gen_implementation: true, + allow_dot_includes: true, } } } @@ -112,6 +115,7 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { let trusted = bridge.unsafety.is_some(); let ref apis = syntax::parse_items(errors, bridge.content, trusted); let ref types = Types::collect(errors, apis); + check::precheck(errors, apis, opt); errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; diff --git a/syntax/error.rs b/syntax/error.rs index a60b7da..9597089 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -19,6 +19,7 @@ pub static ERRORS: &[Error] = &[ CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, DISCRIMINANT_OVERFLOW, + DOT_INCLUDE, DOUBLE_UNDERSCORE, RUST_TYPE_BY_VALUE, USE_NOT_ALLOWED, @@ -54,6 +55,12 @@ pub static DISCRIMINANT_OVERFLOW: Error = Error { 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"), From faa1a7c448916d9f52dbc75fdadd56bd151ed13a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:48:22 +0000 Subject: [PATCH 1073/2232] Merge pull request #375 from dtolnay/include Check for disallowed include strings in Cargo-based workflow --- diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index e6de985..3e37143 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -271,7 +271,10 @@ fn make_include_dir(prj: &Project) -> Result { } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { - let opt = Opt::default(); + let opt = Opt { + allow_dot_includes: false, + ..Opt::default() + }; let generated = gen::generate_from_path(rust_source_file, &opt); let ref rel_path = paths::local_relative_path(rust_source_file); diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index cd58307..c723039 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -66,6 +66,7 @@ fn try_main() -> Result<()> { cxx_impl_annotations: opt.cxx_impl_annotations, gen_header, gen_implementation, + ..Default::default() }; let generated_code = if let Some(input) = opt.input { diff --git a/gen/src/check.rs b/gen/src/check.rs new file mode 100644 index 0000000..35929ad --- /dev/null +++ b/gen/src/check.rs @@ -0,0 +1,27 @@ +use crate::gen::Opt; +use crate::syntax::report::Errors; +use crate::syntax::{error, Api}; +use quote::{quote, quote_spanned}; +use std::path::{Component, Path}; + +pub(super) use crate::syntax::check::typecheck; + +pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) { + if !opt.allow_dot_includes { + check_dot_includes(cx, apis); + } +} + +fn check_dot_includes(cx: &mut Errors, apis: &[Api]) { + for api in apis { + if let Api::Include(include) = api { + let first_component = Path::new(&include.path).components().next(); + if let Some(Component::CurDir) | Some(Component::ParentDir) = first_component { + let begin = quote_spanned!(include.begin_span=> .); + let end = quote_spanned!(include.end_span=> .); + let span = quote!(#begin #end); + cx.error(span, error::DOT_INCLUDE.msg); + } + } + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 1a3febd..236fece 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,6 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. +mod check; pub(super) mod error; mod file; pub(super) mod fs; @@ -13,7 +14,7 @@ use self::error::{format_err, Result}; use self::file::File; use self::include::Include; use crate::syntax::report::Errors; -use crate::syntax::{self, check, Types}; +use crate::syntax::{self, Types}; use std::path::Path; /// Options for C++ code generation. @@ -45,6 +46,7 @@ pub struct Opt { pub(super) gen_header: bool, pub(super) gen_implementation: bool, + pub(super) allow_dot_includes: bool, } /// Results of code generation. @@ -63,6 +65,7 @@ impl Default for Opt { cxx_impl_annotations: None, gen_header: true, gen_implementation: true, + allow_dot_includes: true, } } } @@ -112,6 +115,7 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { let trusted = bridge.unsafety.is_some(); let ref apis = syntax::parse_items(errors, bridge.content, trusted); let ref types = Types::collect(errors, apis); + check::precheck(errors, apis, opt); errors.propagate()?; check::typecheck(errors, namespace, apis, types); errors.propagate()?; diff --git a/syntax/error.rs b/syntax/error.rs index a60b7da..9597089 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -19,6 +19,7 @@ pub static ERRORS: &[Error] = &[ CXX_STRING_BY_VALUE, CXX_TYPE_BY_VALUE, DISCRIMINANT_OVERFLOW, + DOT_INCLUDE, DOUBLE_UNDERSCORE, RUST_TYPE_BY_VALUE, USE_NOT_ALLOWED, @@ -54,6 +55,12 @@ pub static DISCRIMINANT_OVERFLOW: Error = Error { 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"), From 2cc2e3a4d13011e338b1624a6ced35f39f25894d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 20:48:34 +0000 Subject: [PATCH 1074/2232] Include angle brackets in include-related errors --- diff --git a/syntax/parse.rs b/syntax/parse.rs index 5b6abbb..3f579a1 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -6,7 +6,7 @@ use crate::syntax::{ attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Pair, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; -use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; @@ -485,14 +485,10 @@ fn parse_include(input: ParseStream) -> Result { if input.peek(Token![<]) { let mut path = String::new(); - let mut begin_span = None; - let mut end_span = Span::call_site(); - input.parse::()?; + let langle: Token![<] = input.parse()?; while !input.is_empty() && !input.peek(Token![>]) { let token: TokenTree = input.parse()?; - end_span = token.span(); - begin_span = Some(begin_span.unwrap_or(end_span)); match token { TokenTree::Ident(token) => path += &token.to_string(), TokenTree::Literal(token) @@ -507,14 +503,12 @@ fn parse_include(input: ParseStream) -> Result { } } let rangle: Token![>] = input.parse()?; - let begin_span = - begin_span.ok_or_else(|| Error::new(rangle.span, "empty filename in #include"))?; return Ok(Include { path, kind: IncludeKind::Bracketed, - begin_span, - end_span, + begin_span: langle.span, + end_span: rangle.span, }); } From 353d98cdd93772f2a23e2179386ce3091af85f7f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 28 2020 22:43:35 +0000 Subject: [PATCH 1075/2232] Move syntax Include to gen Include conversion to From impl --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 705ca24..309d8c3 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,5 +1,5 @@ use crate::gen::out::OutFile; -use crate::syntax::IncludeKind; +use crate::syntax::{self, IncludeKind}; use std::fmt::{self, Display}; /// The complete contents of the "rust/cxx.h" header. @@ -84,8 +84,8 @@ impl Includes { Includes::default() } - pub fn insert(&mut self, include: Include) { - self.custom.push(include); + pub fn insert(&mut self, include: impl Into) { + self.custom.push(include.into()); } } @@ -95,6 +95,15 @@ impl<'a> Extend<&'a Include> for Includes { } } +impl<'a> From<&'a syntax::Include> for Include { + fn from(include: &syntax::Include) -> Self { + Include { + path: include.path.clone(), + kind: include.kind, + } + } +} + impl Display for Includes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for include in &self.custom { diff --git a/gen/src/write.rs b/gen/src/write.rs index f1c7a11..01617ca 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,3 @@ -use crate::gen::include::Include; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -25,9 +24,7 @@ pub(super) fn gen( out.include.extend(&opt.include); for api in apis { if let Api::Include(include) = api { - let path = include.path.clone(); - let kind = include.kind; - out.include.insert(Include { path, kind }); + out.include.insert(include); } } From dbc5377e9f3c6d29cf82fcf4732005ce17872475 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 29 2020 00:28:51 +0000 Subject: [PATCH 1076/2232] Avoid repeating the underlined type in the label --- diff --git a/syntax/check.rs b/syntax/check.rs index ced1570..25183c9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -67,7 +67,8 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, &format!("unsupported type: {}", ident)); + let msg = format!("unsupported type: {}", ident); + cx.error(ident, &msg); } } diff --git a/syntax/error.rs b/syntax/error.rs index a60b7da..2749277 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -21,6 +21,7 @@ pub static ERRORS: &[Error] = &[ DISCRIMINANT_OVERFLOW, DOUBLE_UNDERSCORE, RUST_TYPE_BY_VALUE, + UNSUPPORTED_TYPE, USE_NOT_ALLOWED, ]; @@ -66,6 +67,12 @@ pub static RUST_TYPE_BY_VALUE: Error = Error { 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"), From 7b14585af9856a7e7bbc042d0292335933c35300 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 29 2020 01:27:36 +0000 Subject: [PATCH 1077/2232] Merge pull request #377 from dtolnay/unsupported-type Include typename in diagnostic on unsupported type --- diff --git a/syntax/check.rs b/syntax/check.rs index 2f3c334..25183c9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -67,7 +67,8 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, "unsupported type"); + let msg = format!("unsupported type: {}", ident); + cx.error(ident, &msg); } } diff --git a/syntax/error.rs b/syntax/error.rs index 9597089..d0ae021 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -22,6 +22,7 @@ pub static ERRORS: &[Error] = &[ DOT_INCLUDE, DOUBLE_UNDERSCORE, RUST_TYPE_BY_VALUE, + UNSUPPORTED_TYPE, USE_NOT_ALLOWED, ]; @@ -73,6 +74,12 @@ pub static RUST_TYPE_BY_VALUE: Error = Error { 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"), From d60c07b7629a373396c4cb15c57c4903402cee27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 29 2020 23:46:40 +0000 Subject: [PATCH 1078/2232] Merge pull request 370 from adetaylor/allow-namespace-override --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 236fece..e6e2c7c 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -6,6 +6,7 @@ pub(super) mod error; mod file; pub(super) mod fs; pub(super) mod include; +mod namespace_organizer; pub(super) mod out; mod write; @@ -113,23 +114,23 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted); + let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); let ref types = Types::collect(errors, apis); check::precheck(errors, apis, opt); errors.propagate()?; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; // Some callers may wish to generate both header and C++ // from the same token stream to avoid parsing twice. But others // only need to generate one or the other. Ok(GeneratedCode { header: if opt.gen_header { - write::gen(namespace, apis, types, opt, true).content() + write::gen(apis, types, opt, true).content() } else { Vec::new() }, implementation: if opt.gen_implementation { - write::gen(namespace, apis, types, opt, false).content() + write::gen(apis, types, opt, false).content() } else { Vec::new() }, diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs new file mode 100644 index 0000000..c54e784 --- /dev/null +++ b/gen/src/namespace_organizer.rs @@ -0,0 +1,40 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::BTreeMap; + +pub(crate) struct NamespaceEntries<'a> { + pub(crate) entries: Vec<&'a Api>, + pub(crate) children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, +} + +pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { + let api_refs = apis.iter().collect::>(); + sort_by_inner_namespace(api_refs, 0) +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: BTreeMap::new(), + }; + + let mut kids_by_child_ns = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } + } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, sort_by_inner_namespace(v, depth + 1)); + } + + root +} diff --git a/gen/src/out.rs b/gen/src/out.rs index d42ea74..8a6bd86 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,10 +1,8 @@ use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { - pub namespace: Namespace, pub header: bool, pub include: Includes, pub front: Content, @@ -18,9 +16,8 @@ pub struct Content { } impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { + pub fn new(header: bool) -> Self { OutFile { - namespace, header, include: Includes::new(), front: Content::new(), diff --git a/gen/src/write.rs b/gen/src/write.rs index 01617ca..967d4ff 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,20 +1,17 @@ +use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::Symbol; -use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var}; +use crate::syntax::{ + mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type, + Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen( - namespace: &Namespace, - apis: &[Api], - types: &Types, - opt: &Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); +pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFile { + let mut out_file = OutFile::new(header); let out = &mut out_file; if header { @@ -32,16 +29,36 @@ pub(super) fn gen( write_include_cxxbridge(out, apis, types); out.next_section(); - for name in namespace { - writeln!(out, "namespace {} {{", name); + + let apis_by_namespace = sort_by_namespace(apis); + + gen_namespace_contents(&apis_by_namespace, types, opt, header, out); + + if !header { + out.next_section(); + write_generic_instantiations(out, types); } + write!(out.front, "{}", out.include); + + out_file +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = &ns_entries.entries; + out.next_section(); for api in apis { match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), - Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), _ => {} } } @@ -51,7 +68,7 @@ pub(super) fn gen( if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type - .entry(&receiver.ty) + .entry(&receiver.ty.rust) .or_insert_with(Vec::new) .push(efn); } @@ -62,22 +79,22 @@ pub(super) fn gen( match api { Api::Struct(strct) => { out.next_section(); - if !types.cxx.contains(&strct.ident) { - write_struct(out, strct); + if !types.cxx.contains(&strct.ident.rust) { + write_struct(out, strct, types); } } Api::Enum(enm) => { out.next_section(); - if types.cxx.contains(&enm.ident) { + if types.cxx.contains(&enm.ident.rust) { check_enum(out, enm); } else { write_enum(out, enm); } } Api::RustType(ety) => { - if let Some(methods) = methods_for_type.get(&ety.ident) { + if let Some(methods) = methods_for_type.get(&ety.ident.rust) { out.next_section(); - write_struct_with_methods(out, ety, methods); + write_struct_with_methods(out, ety, methods, types); } } _ => {} @@ -87,8 +104,8 @@ pub(super) fn gen( out.next_section(); for api in apis { if let Api::TypeAlias(ety) = api { - if types.required_trivial.contains_key(&ety.ident) { - check_trivial_extern_type(out, &ety.ident) + if types.required_trivial.contains_key(&ety.ident.rust) { + check_trivial_extern_type(out, &ety.ident.cxx) } } } @@ -116,24 +133,18 @@ pub(super) fn gen( } out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - if !header { - out.next_section(); - write_generic_instantiations(out, types); + for (child_ns, child_ns_entries) in &ns_entries.children { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_contents(&child_ns_entries, types, opt, header, out); + writeln!(out, "}} // namespace {}", child_ns); } - - write!(out.front, "{}", out.include); - - out_file } fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from(&ident.rust) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, @@ -332,17 +343,17 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, strct.ident); +fn write_struct(out: &mut OutFile, strct: &Struct, types: &Types) { + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident); + writeln!(out, "struct {} final {{", strct.ident.cxx.ident); for field in &strct.fields { write!(out, " "); - write_type_space(out, &field.ty); + write_type_space(out, &field.ty, types); writeln!(out, "{};", field.ident); } writeln!(out, "}};"); @@ -353,25 +364,39 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } -fn write_struct_using(out: &mut OutFile, ident: &Ident) { - writeln!(out, "using {} = {};", ident, ident); +fn write_struct_using(out: &mut OutFile, ident: &CppName) { + writeln!( + out, + "using {} = {};", + ident.ident, + ident.to_fully_qualified() + ); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, ety.ident); +fn write_struct_with_methods( + out: &mut OutFile, + ety: &ExternType, + methods: &[&ExternFn], + types: &Types, +) { + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + writeln!(out, "struct {} final {{", ety.ident.cxx.ident); + writeln!(out, " {}() = delete;", ety.ident.cxx.ident); + writeln!( + out, + " {}(const {} &) = delete;", + ety.ident.cxx.ident, ety.ident.cxx.ident + ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); + let local_name = method.ident.cxx.ident.to_string(); + write_rust_function_shim_decl(out, &local_name, sig, false, types); writeln!(out, ";"); } writeln!(out, "}};"); @@ -379,13 +404,13 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE05_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident); + write!(out, "enum class {} : ", enm.ident.cxx.ident); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -396,7 +421,11 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write!( + out, + "static_assert(sizeof({}) == sizeof(", + enm.ident.cxx.ident + ); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -405,12 +434,12 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, variant.discriminant, + enm.ident.cxx.ident, variant.ident, variant.discriminant, ); } } -fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { +fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { // NOTE: The following two static assertions are just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: @@ -429,6 +458,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { // not being recognized as such by the C++ type system due to a move // constructor or destructor. + let id = &id.to_fully_qualified(); out.include.type_traits = true; writeln!(out, "static_assert("); writeln!( @@ -450,7 +480,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { ); } -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { +fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { let mut has_cxx_throws = false; for api in apis { if let Api::CxxFunction(efn) = api { @@ -486,13 +516,17 @@ fn write_cxx_function_shim( } else { write_extern_return_type_space(out, &efn.ret, types); } - let mangled = mangle::extern_fn(&out.namespace, efn); + let mangled = mangle::extern_fn(efn, types); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!( + out, + "{} &self", + types.resolve(&receiver.ty).to_fully_qualified() + ); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -510,21 +544,26 @@ fn write_cxx_function_shim( if !efn.args.is_empty() || efn.receiver.is_some() { write!(out, ", "); } - write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap(), types); write!(out, "*return$"); } writeln!(out, ") noexcept {{"); write!(out, " "); - write_return_type(out, &efn.ret); + write_return_type(out, &efn.ret, types); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident.rust), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), + Some(receiver) => write!( + out, + "({}::*{}$)(", + types.resolve(&receiver.ty).to_fully_qualified(), + efn.ident.rust + ), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); } write!(out, ")"); if let Some(receiver) = &efn.receiver { @@ -534,8 +573,13 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.cxx), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), + None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()), + Some(receiver) => write!( + out, + "&{}::{}", + types.resolve(&receiver.ty).to_fully_qualified(), + efn.ident.cxx.ident + ), } writeln!(out, ";"); write!(out, " "); @@ -548,7 +592,7 @@ fn write_cxx_function_shim( if indirect_return { out.include.new = true; write!(out, "new (return$) "); - write_indirect_return_type(out, efn.ret.as_ref().unwrap()); + write_indirect_return_type(out, efn.ret.as_ref().unwrap(), types); write!(out, "("); } else if efn.ret.is_some() { write!(out, "return "); @@ -570,10 +614,10 @@ fn write_cxx_function_shim( write!(out, ", "); } if let Type::RustBox(_) = &arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "::from_raw({})", arg.ident); } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "({})", arg.ident); } else if arg.ty == RustString { write!( @@ -582,7 +626,7 @@ fn write_cxx_function_shim( arg.ident, ); } else if let Type::RustVec(_) = arg.ty { - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if types.needs_indirect_abi(&arg.ty) { out.include.utility = true; @@ -632,17 +676,17 @@ fn write_function_pointer_trampoline( types: &Types, ) { out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let r_trampoline = mangle::r_trampoline(efn, var, types); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + let c_trampoline = mangle::c_trampoline(efn, var, types).to_string(); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { - let link_name = mangle::extern_fn(&out.namespace, efn); + let link_name = mangle::extern_fn(efn, types); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -665,7 +709,11 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!( + out, + "{} &self", + types.resolve(&receiver.ty).to_fully_qualified() + ); needs_comma = true; } for arg in &sig.args { @@ -679,7 +727,7 @@ fn write_rust_function_decl_impl( if needs_comma { write!(out, ", "); } - write_return_type(out, &sig.ret); + write_return_type(out, &sig.ret, types); write!(out, "*return$"); needs_comma = true; } @@ -697,10 +745,14 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), + None => efn.ident.cxx.ident.to_string(), + Some(receiver) => format!( + "{}::{}", + types.resolve(&receiver.ty).ident, + efn.ident.cxx.ident + ), }; - let invoke = mangle::extern_fn(&out.namespace, efn); + let invoke = mangle::extern_fn(efn, types); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } @@ -710,14 +762,15 @@ fn write_rust_function_shim_decl( local_name: &str, sig: &Signature, indirect_call: bool, + types: &Types, ) { - write_return_type(out, &sig.ret); + write_return_type(out, &sig.ret, types); write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } - write_type_space(out, &arg.ty); + write_type_space(out, &arg.ty, types); write!(out, "{}", arg.ident); } if indirect_call { @@ -749,7 +802,7 @@ fn write_rust_function_shim_impl( // We've already defined this inside the struct. return; } - write_rust_function_shim_decl(out, local_name, sig, indirect_call); + write_rust_function_shim_decl(out, local_name, sig, indirect_call, types); if out.header { writeln!(out, ";"); return; @@ -759,7 +812,7 @@ fn write_rust_function_shim_impl( if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); writeln!(out, "> {}$(::std::move({0}));", arg.ident); } } @@ -767,18 +820,18 @@ fn write_rust_function_shim_impl( let indirect_return = indirect_return(sig, types); if indirect_return { write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap()); + write_type(out, sig.ret.as_ref().unwrap(), types); writeln!(out, "> return$;"); write!(out, " "); } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { - write_type(out, ret); + write_type(out, ret, types); write!(out, "::from_raw("); } Type::UniquePtr(_) => { - write_type(out, ret); + write_type(out, ret, types); write!(out, "("); } Type::Ref(_) => write!(out, "*"), @@ -844,10 +897,10 @@ fn write_rust_function_shim_impl( writeln!(out, "}}"); } -fn write_return_type(out: &mut OutFile, ty: &Option) { +fn write_return_type(out: &mut OutFile, ty: &Option, types: &Types) { match ty { None => write!(out, "void "), - Some(ty) => write_type_space(out, ty), + Some(ty) => write_type_space(out, ty, types), } } @@ -857,27 +910,27 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } -fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { +fn write_indirect_return_type(out: &mut OutFile, ty: &Type, types: &Types) { match ty { Type::RustBox(ty) | Type::UniquePtr(ty) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Type::Ref(ty) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), - _ => write_type(out, ty), + _ => write_type(out, ty, types), } } -fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { - write_indirect_return_type(out, ty); +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type, types: &Types) { + write_indirect_return_type(out, ty, types); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), @@ -888,32 +941,32 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { match ty { Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Some(Type::Ref(ty)) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), - _ => write_return_type(out, ty), + _ => write_return_type(out, ty, types), } } fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { match &arg.ty { Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { - write_type_space(out, &ty.inner); + write_type_space(out, &ty.inner, types); write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), - _ => write_type_space(out, &arg.ty), + _ => write_type_space(out, &arg.ty, types), } if types.needs_indirect_abi(&arg.ty) { write!(out, "*"); @@ -921,37 +974,37 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { write!(out, "{}", arg.ident); } -fn write_type(out: &mut OutFile, ty: &Type) { +fn write_type(out: &mut OutFile, ty: &Type, types: &Types) { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from(&ident.rust) { Some(atom) => write_atom(out, atom), - None => write!(out, "{}", ident), + None => write!(out, "{}", types.resolve(ident).to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::RustVec(ty) => { write!(out, "::rust::Vec<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner); + write_type(out, &ptr.inner, types); write!(out, ">"); } Type::CxxVector(ty) => { write!(out, "::std::vector<"); - write_type(out, &ty.inner); + write_type(out, &ty.inner, types); write!(out, ">"); } Type::Ref(r) => { if r.mutability.is_none() { write!(out, "const "); } - write_type(out, &r.inner); + write_type(out, &r.inner, types); write!(out, " &"); } Type::Slice(_) => { @@ -967,7 +1020,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { - Some(ret) => write_type(out, ret), + Some(ret) => write_type(out, ret, types), None => write!(out, "void"), } write!(out, "("); @@ -975,7 +1028,7 @@ fn write_type(out: &mut OutFile, ty: &Type) { if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty); + write_type(out, &arg.ty, types); } write!(out, ")>"); } @@ -1003,8 +1056,8 @@ fn write_atom(out: &mut OutFile, atom: Atom) { } } -fn write_type_space(out: &mut OutFile, ty: &Type) { - write_type(out, ty); +fn write_type_space(out: &mut OutFile, ty: &Type, types: &Types) { + write_type(out, ty, types); write_space_after_type(out, ty); } @@ -1025,28 +1078,20 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { +fn to_typename(ty: &Type, types: &Types) -> String { match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + Type::Ident(ident) => types.resolve(&ident).to_fully_qualified(), + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner, types)), _ => unreachable!(), } } // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { +fn to_mangled(ty: &Type, types: &Types) -> Symbol { match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_symbol(types), + Type::CxxVector(ptr) => to_mangled(&ptr.inner, types).prefix_with("std$vector$"), _ => unreachable!(), } } @@ -1057,19 +1102,20 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { out.next_section(); - write_rust_box_extern(out, inner); + write_rust_box_extern(out, &types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(&inner.rust).is_none() { out.next_section(); - write_rust_vec_extern(out, inner); + write_rust_vec_extern(out, inner, types); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() - && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + if Atom::from(&inner.rust).is_none() + && (!types.aliases.contains_key(&inner.rust) + || types.explicit_impls.contains(ty)) { out.next_section(); write_unique_ptr(out, inner, types); @@ -1077,8 +1123,9 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() - && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) + if Atom::from(&inner.rust).is_none() + && (!types.aliases.contains_key(&inner.rust) + || types.explicit_impls.contains(ty)) { out.next_section(); write_cxx_vector(out, ty, inner, types); @@ -1093,12 +1140,12 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, inner); + write_rust_box_impl(out, &types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { - write_rust_vec_impl(out, inner); + if Atom::from(&inner.rust).is_none() { + write_rust_vec_impl(out, inner, types); } } } @@ -1107,14 +1154,9 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("namespace rust"); } -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); @@ -1131,10 +1173,10 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); @@ -1166,14 +1208,9 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_impl(out: &mut OutFile, ident: &CppName) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); @@ -1186,10 +1223,10 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); @@ -1225,9 +1262,9 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName, types: &Types) { let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); + let instance = to_mangled(&ty, types); writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); @@ -1241,8 +1278,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { out.include.new = true; out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); + let inner = to_typename(ty, types); + let instance = to_mangled(ty, types); let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. We can't @@ -1250,7 +1287,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { // bindings for a "new" method anyway. But the Rust code can't be called // for Opaque types because the 'new' method is not implemented. Type::Ident(ident) => { - types.structs.contains_key(ident) || types.aliases.contains_key(ident) + types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) } _ => false, }; @@ -1315,10 +1352,10 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element, types); + let instance = to_mangled(&element, types); writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4a4ec64..1bcaa1e 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,12 +1,11 @@ use crate::derive::DeriveAttribute; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::file::Module; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, - Types, + self, check, mangle, Api, CppName, Enum, ExternFn, ExternType, Impl, ResolvableName, Signature, + Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -17,11 +16,11 @@ pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, content, trusted); + let namespace = &ffi.namespace; + let ref apis = syntax::parse_items(errors, content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - let namespace = &ffi.namespace; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; Ok(expand(ffi, apis, types)) @@ -30,7 +29,6 @@ pub fn bridge(mut ffi: Module) -> Result { fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { @@ -42,23 +40,23 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), - Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), + Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { let ident = &ety.ident; - if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { - expanded.extend(expand_cxx_type(namespace, ety)); + if !types.structs.contains_key(&ident.rust) + && !types.enums.contains_key(&ident.rust) + { + expanded.extend(expand_cxx_type(ety)); } } Api::CxxFunction(efn) => { - expanded.extend(expand_cxx_function_shim(namespace, efn, types)); - } - Api::RustFunction(efn) => { - hidden.extend(expand_rust_function_shim(namespace, efn, types)) + expanded.extend(expand_cxx_function_shim(efn, types)); } + Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias, types)); + hidden.extend(expand_type_alias_verify(alias, types)); } } } @@ -67,33 +65,33 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_box(namespace, ident)); + if Atom::from(&ident.rust).is_none() { + hidden.extend(expand_rust_box(ident, types)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_vec(namespace, ident)); + if Atom::from(&ident.rust).is_none() { + hidden.extend(expand_rust_vec(ident, types)); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() - && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) + if Atom::from(&ident.rust).is_none() + && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust)) { - expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); + expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() - && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) + if Atom::from(&ident.rust).is_none() + && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust)) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); + expanded.extend(expand_cxx_vector(ident, explicit_impl, types)); } } } @@ -126,11 +124,12 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { - let ident = &strct.ident; +fn expand_struct(strct: &Struct) -> TokenStream { + let ident = &strct.ident.rust; + let cxx_ident = &strct.ident.cxx; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(namespace, ident); + let type_id = type_id(cxx_ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -153,11 +152,12 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { } } -fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { - let ident = &enm.ident; +fn expand_enum(enm: &Enum) -> TokenStream { + let ident = &enm.ident.rust; + let cxx_ident = &enm.ident.cxx; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(namespace, ident); + let type_id = type_id(cxx_ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -186,10 +186,11 @@ fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { - let ident = &ety.ident; +fn expand_cxx_type(ety: &ExternType) -> TokenStream { + let ident = &ety.ident.rust; + let cxx_ident = &ety.ident.cxx; let doc = &ety.doc; - let type_id = type_id(namespace, ident); + let type_id = type_id(&cxx_ident); quote! { #doc @@ -205,7 +206,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } } -fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -236,7 +237,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = mangle::extern_fn(namespace, efn); + let link_name = mangle::extern_fn(efn, types); let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] @@ -244,9 +245,9 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } } -fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; - let decl = expand_cxx_function_decl(namespace, efn, types); + let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver.iter().map(|receiver| { let ampersand = receiver.ampersand; let mutability = receiver.mutability; @@ -272,14 +273,14 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types let arg_vars = efn.args.iter().map(|arg| { let var = &arg.ident; match &arg.ty { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString) } Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => quote!(::cxx::private::RustString::from_ref(#var)), Some(_) => quote!(::cxx::private::RustString::from_mut(#var)), }, @@ -306,9 +307,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .filter_map(|arg| { if let Type::Fn(f) = &arg.ty { let var = &arg.ident; - Some(expand_function_pointer_trampoline( - namespace, efn, var, f, types, - )) + Some(expand_function_pointer_trampoline(efn, var, f, types)) } else { None } @@ -355,7 +354,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }; let expr = if efn.throws { efn.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { Some(quote!(#call.map(|r| r.into_string()))) } Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))), @@ -368,7 +367,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(#call.map(|r| r.as_string()))), Some(_) => Some(quote!(#call.map(|r| r.as_mut_string()))), }, @@ -388,7 +387,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types }) } else { efn.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())), + Type::Ident(ident) if ident.rust == RustString => Some(quote!(#call.into_string())), Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))), Type::RustVec(vec) => { if vec.inner == RustString { @@ -399,7 +398,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(#call.as_string())), Some(_) => Some(quote!(#call.as_mut_string())), }, @@ -445,14 +444,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_function_pointer_trampoline( - namespace: &Namespace, efn: &ExternFn, var: &Ident, sig: &Signature, types: &Types, ) -> TokenStream { - let c_trampoline = mangle::c_trampoline(namespace, efn, var); - let r_trampoline = mangle::r_trampoline(namespace, efn, var); + let c_trampoline = mangle::c_trampoline(efn, var, types); + let r_trampoline = mangle::r_trampoline(efn, var, types); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( @@ -500,7 +498,7 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { let sized = quote_spanned! {ety.semi_token.span=> #begin_span std::marker::Sized }; - quote_spanned! {ident.span()=> + quote_spanned! {ident.rust.span()=> let _ = { fn __AssertSized() {} __AssertSized::<#ident> @@ -508,8 +506,8 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } } -fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let link_name = mangle::extern_fn(namespace, efn); +fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { + let link_name = mangle::extern_fn(efn, types); let local_name = format_ident!("__{}", efn.ident.rust); let catch_unwind_label = format!("::{}", efn.ident.rust); let invoke = Some(&efn.ident.rust); @@ -553,7 +551,7 @@ fn expand_rust_function_shim_impl( let arg_vars = sig.args.iter().map(|arg| { let ident = &arg.ident; match &arg.ty { - Type::Ident(i) if i == RustString => { + Type::Ident(i) if i.rust == RustString => { quote!(::std::mem::take((*#ident).as_mut_string())) } Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), @@ -566,7 +564,7 @@ fn expand_rust_function_shim_impl( } Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), Type::Ref(ty) => match &ty.inner { - Type::Ident(i) if i == RustString => match ty.mutability { + Type::Ident(i) if i.rust == RustString => match ty.mutability { None => quote!(#ident.as_string()), Some(_) => quote!(#ident.as_mut_string()), }, @@ -601,7 +599,9 @@ fn expand_rust_function_shim_impl( call.extend(quote! { (#(#vars),*) }); let conversion = sig.ret.as_ref().and_then(|ret| match ret { - Type::Ident(ident) if ident == RustString => Some(quote!(::cxx::private::RustString::from)), + Type::Ident(ident) if ident.rust == RustString => { + Some(quote!(::cxx::private::RustString::from)) + } Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)), Type::RustVec(vec) => { if vec.inner == RustString { @@ -612,7 +612,7 @@ fn expand_rust_function_shim_impl( } Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { - Type::Ident(ident) if ident == RustString => match ty.mutability { + Type::Ident(ident) if ident.rust == RustString => match ty.mutability { None => Some(quote!(::cxx::private::RustString::from_ref)), Some(_) => Some(quote!(::cxx::private::RustString::from_mut)), }, @@ -686,13 +686,9 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify( - namespace: &Namespace, - alias: &TypeAlias, - types: &Types, -) -> TokenStream { +fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.ident; - let type_id = type_id(namespace, ident); + let type_id = type_id(&ident.cxx); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); @@ -702,7 +698,7 @@ fn expand_type_alias_verify( const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial.contains_key(&alias.ident) { + if types.required_trivial.contains_key(&alias.ident.rust) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; @@ -712,25 +708,19 @@ fn expand_type_alias_verify( verify } -fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - +fn type_id(ident: &CppName) -> TokenStream { + let path = ident.to_fully_qualified(); quote! { ::cxx::type_id!(#path) } } -fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); +fn expand_rust_box(ident: &ResolvableName, types: &Types) -> TokenStream { + let link_prefix = format!("cxxbridge05$box${}$", types.resolve(ident).to_symbol()); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); - let local_prefix = format_ident!("{}__box_", ident); + let local_prefix = format_ident!("{}__box_", &ident.rust); let local_uninit = format_ident!("{}uninit", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); @@ -754,15 +744,15 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); +fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream { + let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types)); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); let link_data = format!("{}data", link_prefix); let link_stride = format!("{}stride", link_prefix); - let local_prefix = format_ident!("{}__vec_", elem); + let local_prefix = format_ident!("{}__vec_", elem.rust); let local_new = format_ident!("{}new", local_prefix); let local_drop = format_ident!("{}drop", local_prefix); let local_len = format_ident!("{}len", local_prefix); @@ -800,13 +790,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { } fn expand_unique_ptr( - namespace: &Namespace, - ident: &Ident, + ident: &ResolvableName, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge05$unique_ptr${}{}$", namespace, ident); + let name = ident.rust.to_string(); + let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types)); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -814,21 +803,22 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { - Some(quote! { - fn __new(mut value: Self) -> *mut ::std::ffi::c_void { - extern "C" { - #[link_name = #link_new] - fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + let new_method = + if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) { + Some(quote! { + fn __new(mut value: Self) -> *mut ::std::ffi::c_void { + extern "C" { + #[link_name = #link_new] + fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); + } + let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + unsafe { __new(&mut repr, &mut value) } + repr } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); - unsafe { __new(&mut repr, &mut value) } - repr - } - }) - } else { - None - }; + }) + } else { + None + }; let begin_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span); @@ -883,16 +873,19 @@ fn expand_unique_ptr( } fn expand_cxx_vector( - namespace: &Namespace, - elem: &Ident, + elem: &ResolvableName, explicit_impl: Option<&Impl>, + types: &Types, ) -> TokenStream { let _ = explicit_impl; - let name = elem.to_string(); - let prefix = format!("cxxbridge05$std$vector${}{}$", namespace, elem); + let name = elem.rust.to_string(); + let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol(types)); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!( + "cxxbridge05$unique_ptr$std$vector${}$", + elem.to_symbol(types) + ); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); @@ -979,7 +972,7 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { fn expand_extern_type(ty: &Type) -> TokenStream { match ty { - Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString), + Type::Ident(ident) if ident.rust == RustString => quote!(::cxx::private::RustString), Type::RustBox(ty) | Type::UniquePtr(ty) => { let inner = expand_extern_type(&ty.inner); quote!(*mut #inner) @@ -991,7 +984,7 @@ fn expand_extern_type(ty: &Type) -> TokenStream { Type::Ref(ty) => { let mutability = ty.mutability; match &ty.inner { - Type::Ident(ident) if ident == RustString => { + Type::Ident(ident) if ident.rust == RustString => { quote!(&#mutability ::cxx::private::RustString) } Type::RustVec(ty) => { diff --git a/syntax/atom.rs b/syntax/atom.rs index 6e5fa88..7d0ef6b 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -81,7 +81,7 @@ impl AsRef for Atom { impl PartialEq for Type { fn eq(&self, atom: &Atom) -> bool { match self { - Type::Ident(ident) => ident == atom, + Type::Ident(ident) => ident.rust == atom, _ => false, } } diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4c8a3e5..25af229 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,3 +1,4 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::Atom::{self, *}; use crate::syntax::{Derive, Doc}; @@ -12,19 +13,7 @@ pub struct Parser<'a> { pub repr: Option<&'a mut Option>, pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, -} - -pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc { - let mut doc = Doc::new(); - parse( - cx, - attrs, - Parser { - doc: Some(&mut doc), - ..Parser::default() - }, - ); - doc + pub namespace: Option<&'a mut Namespace>, } pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { @@ -79,6 +68,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) { } Err(err) => return cx.push(err), } + } 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) => return cx.push(err), + } } return cx.error(attr, "unsupported attribute"); } @@ -131,3 +130,10 @@ fn parse_function_alias_attribute(input: ParseStream) -> Result { input.parse() } } + +fn parse_namespace_attribute(input: ParseStream) -> Result { + let content; + syn::parenthesized!(content in input); + let namespace = content.parse::()?; + Ok(namespace) +} diff --git a/syntax/check.rs b/syntax/check.rs index 25183c9..08e0dfa 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,5 +1,4 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ @@ -11,15 +10,13 @@ use quote::{quote, ToTokens}; use std::fmt::Display; pub(crate) struct Check<'a> { - namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, } -pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { +pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) { do_typecheck(&mut Check { - namespace, apis, types, errors: cx, @@ -27,11 +24,11 @@ pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], ty } fn do_typecheck(cx: &mut Check) { - ident::check_all(cx, cx.namespace, cx.apis); + ident::check_all(cx, cx.apis); for ty in cx.types { match ty { - Type::Ident(ident) => check_type_ident(cx, ident), + Type::Ident(ident) => check_type_ident(cx, &ident.rust), 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), @@ -74,14 +71,14 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { fn check_type_box(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.cxx.contains(ident) - && !cx.types.structs.contains_key(ident) - && !cx.types.enums.contains_key(ident) + if cx.types.cxx.contains(&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).is_none() { + if Atom::from(&ident.rust).is_none() { return; } } @@ -91,15 +88,15 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { if let Type::Ident(ident) = &ty.inner { - if cx.types.cxx.contains(ident) - && !cx.types.structs.contains_key(ident) - && !cx.types.enums.contains_key(ident) + if cx.types.cxx.contains(&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) { + 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(RustString) => return, @@ -113,11 +110,11 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.rust.contains(ident) { + if cx.types.rust.contains(&ident.rust) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); } - match Atom::from(ident) { + match Atom::from(&ident.rust) { None | Some(CxxString) => return, _ => {} } @@ -130,14 +127,14 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { if let Type::Ident(ident) = &ptr.inner { - if cx.types.rust.contains(ident) { + if cx.types.rust.contains(&ident.rust) { cx.error( ptr, "C++ vector containing a Rust type is not supported yet", ); } - match Atom::from(ident) { + 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, @@ -171,15 +168,15 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { fn check_api_struct(cx: &mut Check, strct: &Struct) { let ident = &strct.ident; - check_reserved_name(cx, ident); + check_reserved_name(cx, &ident.rust); 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(ident) { - if let Some(ety) = cx.types.untrusted.get(ident) { + if cx.types.cxx.contains(&ident.rust) { + if let Some(ety) = cx.types.untrusted.get(&ident.rust) { let msg = "extern shared struct must be declared in an `unsafe extern` block"; cx.error(ety, msg); } @@ -201,7 +198,7 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } fn check_api_enum(cx: &mut Check, enm: &Enum) { - check_reserved_name(cx, &enm.ident); + check_reserved_name(cx, &enm.ident.rust); if enm.variants.is_empty() { let span = span_for_enum_error(enm); @@ -210,11 +207,13 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } fn check_api_type(cx: &mut Check, ety: &ExternType) { - check_reserved_name(cx, &ety.ident); + check_reserved_name(cx, &ety.ident.rust); - if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { + if let Some(reason) = cx.types.required_trivial.get(&ety.ident.rust) { let what = match reason { - TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::StructField(strct) => { + format!("a field of `{}`", strct.ident.cxx.to_fully_qualified()) + } TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; @@ -230,7 +229,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); - if receiver.ty == "Self" { + if receiver.ty.is_self() { let mutability = match receiver.mutability { Some(_) => "mut ", None => "", @@ -242,9 +241,9 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { mutability = mutability, ); cx.error(span, msg); - } else if !cx.types.structs.contains_key(&receiver.ty) - && !cx.types.cxx.contains(&receiver.ty) - && !cx.types.rust.contains(&receiver.ty) + } 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"); } @@ -291,7 +290,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { fn check_api_impl(cx: &mut Check, imp: &Impl) { if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(&inner.rust).is_none() { return; } } @@ -358,7 +357,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { fn is_unsized(cx: &mut Check, ty: &Type) -> bool { let ident = match ty { - Type::Ident(ident) => ident, + Type::Ident(ident) => &ident.rust, Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true, _ => return false, }; @@ -401,20 +400,20 @@ fn span_for_receiver_error(receiver: &Receiver) -> TokenStream { fn describe(cx: &mut Check, ty: &Type) -> String { match ty { Type::Ident(ident) => { - if cx.types.structs.contains_key(ident) { + if cx.types.structs.contains_key(&ident.rust) { "struct".to_owned() - } else if cx.types.enums.contains_key(ident) { + } else if cx.types.enums.contains_key(&ident.rust) { "enum".to_owned() - } else if cx.types.aliases.contains_key(ident) { + } else if cx.types.aliases.contains_key(&ident.rust) { "C++ type".to_owned() - } else if cx.types.cxx.contains(ident) { + } else if cx.types.cxx.contains(&ident.rust) { "opaque C++ type".to_owned() - } else if cx.types.rust.contains(ident) { + } else if cx.types.rust.contains(&ident.rust) { "opaque Rust type".to_owned() - } else if Atom::from(ident) == Some(CxxString) { + } else if Atom::from(&ident.rust) == Some(CxxString) { "C++ string".to_owned() } else { - ident.to_string() + ident.rust.to_string() } } Type::RustBox(_) => "Box".to_owned(), diff --git a/syntax/ident.rs b/syntax/ident.rs index 66f7365..354790a 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,6 +1,5 @@ use crate::syntax::check::Check; -use crate::syntax::namespace::Namespace; -use crate::syntax::{error, Api}; +use crate::syntax::{error, Api, CppName}; use proc_macro2::Ident; fn check(cx: &mut Check, ident: &Ident) { @@ -13,28 +12,31 @@ fn check(cx: &mut Check, ident: &Ident) { } } -pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { - for segment in namespace { +fn check_ident(cx: &mut Check, ident: &CppName) { + for segment in &ident.ns { check(cx, segment); } + check(cx, &ident.ident); +} +pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check(cx, &strct.ident); + check_ident(cx, &strct.ident.cxx); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check(cx, &enm.ident); + check_ident(cx, &enm.ident.cxx); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check(cx, &ety.ident); + check_ident(cx, &ety.ident.cxx); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.ident.rust); @@ -43,7 +45,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { } } Api::TypeAlias(alias) => { - check(cx, &alias.ident); + check_ident(cx, &alias.ident.cxx); } } } diff --git a/syntax/impls.rs b/syntax/impls.rs index a4b393a..424ad9d 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,8 +1,13 @@ -use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type}; +use crate::syntax::{ + Api, CppName, ExternFn, Impl, Include, Namespace, Pair, Receiver, Ref, ResolvableName, + Signature, Slice, Symbol, Ty1, Type, Types, +}; +use proc_macro2::{Ident, Span}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; +use syn::Token; impl PartialEq for Include { fn eq(&self, other: &Include) -> bool { @@ -292,3 +297,84 @@ impl Borrow for &Impl { &self.ty } } + +impl Pair { + /// Use this constructor when the item can't have a different + /// name in Rust and C++. For cases where #[rust_name] and similar + /// attributes can be used, construct the object by hand. + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { + rust: ident.clone(), + cxx: CppName::new(ns, ident), + } + } +} + +impl ResolvableName { + pub fn new(ident: Ident) -> Self { + Self { rust: ident } + } + + pub fn from_pair(pair: Pair) -> Self { + Self { rust: pair.rust } + } + + pub fn make_self(span: Span) -> Self { + Self { + rust: Token![Self](span).into(), + } + } + + pub fn is_self(&self) -> bool { + self.rust == "Self" + } + + pub fn span(&self) -> Span { + self.rust.span() + } + + pub fn to_symbol(&self, types: &Types) -> Symbol { + types.resolve(self).to_symbol() + } +} + +impl Api { + pub fn get_namespace(&self) -> Option<&Namespace> { + match self { + Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), + Api::CxxType(cty) => Some(&cty.ident.cxx.ns), + Api::Enum(enm) => Some(&enm.ident.cxx.ns), + Api::Struct(strct) => Some(&strct.ident.cxx.ns), + Api::RustType(rty) => Some(&rty.ident.cxx.ns), + Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), + Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, + } + } +} + +impl CppName { + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { ns, ident } + } + + fn iter_all_segments( + &self, + ) -> std::iter::Chain, std::iter::Once<&Ident>> { + self.ns.iter().chain(std::iter::once(&self.ident)) + } + + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } + + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) + } +} diff --git a/syntax/mangle.rs b/syntax/mangle.rs index e461887..9255feb 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,6 +1,5 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::{self, Symbol}; -use crate::syntax::ExternFn; +use crate::syntax::{ExternFn, Types}; use proc_macro2::Ident; const CXXBRIDGE: &str = "cxxbridge05"; @@ -11,19 +10,27 @@ macro_rules! join { }; } -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { +pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), - None => join!(namespace, CXXBRIDGE, efn.ident.rust), + Some(receiver) => { + let receiver_ident = types.resolve(&receiver.ty); + join!( + efn.ident.cxx.ns, + CXXBRIDGE, + receiver_ident.ident, + efn.ident.rust + ) + } + None => join!(efn.ident.cxx.ns, CXXBRIDGE, efn.ident.rust), } } // The C half of a function pointer trampoline. -pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 0) +pub fn c_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol { + join!(extern_fn(efn, types), var, 0) } // The Rust half of a function pointer trampoline. -pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 1) +pub fn r_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol { + join!(extern_fn(efn, types), var, 1) } diff --git a/syntax/mod.rs b/syntax/mod.rs index 180c40a..6498d74 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -21,7 +21,9 @@ mod tokens; pub mod types; use self::discriminant::Discriminant; +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}; @@ -33,6 +35,29 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; +/// A Rust identifier will forver == a proc_macro2::Ident, +/// but for completeness here's a type alias. +pub type RsIdent = Ident; + +/// At the moment, a Rust name is simply a proc_macro2::Ident. +/// In the future, it may become namespaced based on a mod path. +pub type RsName = RsIdent; + +/// At the moment, a C++ identifier is also a proc_macro2::Ident. +/// In the future, we may wish to make a newtype wrapper here +/// to avoid confusion between C++ and Rust identifiers. +pub type CppIdent = Ident; + +#[derive(Clone)] +/// A C++ identifier in a particular namespace. +/// It is intentional that this does not impl Display, +/// because we want to force users actively to decide whether to output +/// it as a qualified name or as an unqualfiied name. +pub struct CppName { + pub ns: Namespace, + pub ident: CppIdent, +} + pub enum Api { Include(Include), Struct(Struct), @@ -64,7 +89,7 @@ pub enum IncludeKind { pub struct ExternType { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: Pair, pub semi_token: Token![;], pub trusted: bool, } @@ -73,7 +98,7 @@ pub struct Struct { pub doc: Doc, pub derives: Vec, pub struct_token: Token![struct], - pub ident: Ident, + pub ident: Pair, pub brace_token: Brace, pub fields: Vec, } @@ -81,15 +106,18 @@ pub struct Struct { pub struct Enum { pub doc: Doc, pub enum_token: Token![enum], - pub ident: Ident, + pub ident: Pair, pub brace_token: Brace, pub variants: Vec, pub repr: Atom, } +/// A type with a defined Rust name and a fully resolved, +/// qualified, namespaced, C++ name. +#[derive(Clone)] pub struct Pair { - pub cxx: Ident, - pub rust: Ident, + pub cxx: CppName, + pub rust: RsName, } pub struct ExternFn { @@ -103,7 +131,7 @@ pub struct ExternFn { pub struct TypeAlias { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: Pair, pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], @@ -128,7 +156,7 @@ pub struct Signature { #[derive(Eq, PartialEq, Hash)] pub struct Var { - pub ident: Ident, + pub ident: RsIdent, // fields and variables are not namespaced pub ty: Type, } @@ -137,18 +165,18 @@ pub struct Receiver { pub lifetime: Option, pub mutability: Option, pub var: Token![self], - pub ty: Ident, + pub ty: ResolvableName, pub shorthand: bool, } pub struct Variant { - pub ident: Ident, + pub ident: RsIdent, pub discriminant: Discriminant, pub expr: Option, } pub enum Type { - Ident(Ident), + Ident(ResolvableName), RustBox(Box), RustVec(Box), UniquePtr(Box), @@ -162,7 +190,7 @@ pub enum Type { } pub struct Ty1 { - pub name: Ident, + pub name: ResolvableName, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], @@ -185,3 +213,10 @@ pub enum Lang { Cxx, Rust, } + +/// Wrapper for a type which needs to be resolved +/// before it can be printed in C++. +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct ResolvableName { + pub rust: RsName, +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 3f579a1..7a56ab8 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,9 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Pair, - Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, CppName, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, + Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias, + Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -20,20 +21,22 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec { +pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namespace) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(cx, item) { + Item::Struct(item) => match parse_struct(cx, item, ns.clone()) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(cx, item) { + Item::Enum(item) => match parse_enum(cx, item, ns.clone()) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, - Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), - Item::Impl(item) => match parse_impl(item) { + Item::ForeignMod(foreign_mod) => { + parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, ns) + } + Item::Impl(item) => match parse_impl(item, ns) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, @@ -44,7 +47,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec apis } -fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -65,6 +68,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -81,7 +85,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { doc, derives, struct_token: item.struct_token, - ident: item.ident, + ident: Pair::new(ns.clone(), item.ident), brace_token: fields.brace_token, fields: fields .named @@ -89,14 +93,14 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { .map(|field| { Ok(Var { ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, + ty: parse_type(&field.ty, &ns)?, }) }) .collect::>()?, })) } -fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum, mut ns: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -117,6 +121,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { attrs::Parser { doc: Some(&mut doc), repr: Some(&mut repr), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -167,7 +172,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { Ok(Api::Enum(Enum { doc, enum_token, - ident: item.ident, + ident: Pair::new(ns, item.ident), brace_token, variants, repr, @@ -179,6 +184,7 @@ fn parse_foreign_mod( foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, + ns: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, @@ -202,11 +208,13 @@ fn parse_foreign_mod( let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted) { - Ok(ety) => items.push(ety), - Err(err) => cx.push(err), - }, - ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) { + ForeignItem::Type(foreign) => { + match parse_extern_type(cx, foreign, lang, trusted, ns.clone()) { + Ok(ety) => items.push(ety), + Err(err) => cx.push(err), + } + } + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, ns.clone()) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -216,10 +224,12 @@ fn parse_foreign_mod( Err(err) => cx.push(err), } } - ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) { - Ok(api) => items.push(api), - Err(err) => cx.push(err), - }, + ForeignItem::Verbatim(tokens) => { + match parse_extern_verbatim(cx, tokens, lang, ns.clone()) { + Ok(api) => items.push(api), + Err(err) => cx.push(err), + } + } _ => cx.error(foreign, "unsupported foreign item"), } } @@ -234,8 +244,8 @@ fn parse_foreign_mod( for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { - if receiver.ty == "Self" { - receiver.ty = single_type.clone(); + if receiver.ty.is_self() { + receiver.ty = ResolvableName::from_pair(single_type.clone()); } } } @@ -267,8 +277,18 @@ fn parse_extern_type( foreign_type: &ForeignItemType, lang: Lang, trusted: bool, + mut ns: Namespace, ) -> Result { - let doc = attrs::parse_doc(cx, &foreign_type.attrs); + let mut doc = Doc::new(); + attrs::parse( + cx, + &foreign_type.attrs, + attrs::Parser { + doc: Some(&mut doc), + namespace: Some(&mut ns), + ..Default::default() + }, + ); let type_token = foreign_type.type_token; let ident = foreign_type.ident.clone(); let semi_token = foreign_type.semi_token; @@ -279,13 +299,18 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident, + ident: Pair::new(ns, ident), semi_token, trusted, })) } -fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + cx: &mut Errors, + foreign_fn: &ForeignItemFn, + lang: Lang, + mut ns: Namespace, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -310,6 +335,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R doc: Some(&mut doc), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), + namespace: Some(&mut ns), ..Default::default() }, ); @@ -326,7 +352,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R lifetime: lifetime.clone(), mutability: arg.mutability, var: arg.self_token, - ty: Token![Self](arg.self_token.span).into(), + ty: ResolvableName::make_self(arg.self_token.span), shorthand: true, }); continue; @@ -341,7 +367,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, &ns)?; if ident != "self" { args.push_value(Var { ident, ty }); if let Some(comma) = comma { @@ -355,7 +381,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R ampersand: reference.ampersand, lifetime: reference.lifetime, mutability: reference.mutability, - var: Token![self](ident.span()), + var: Token![self](ident.rust.span()), ty: ident, shorthand: false, }); @@ -368,12 +394,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } let mut throws_tokens = None; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &ns)?; let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = Pair { - cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + cxx: CppName::new(ns, cxx_name.unwrap_or(foreign_fn.sig.ident.clone())), rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), }; let paren_token = foreign_fn.sig.paren_token; @@ -401,7 +427,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R })) } -fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { +fn parse_extern_verbatim( + cx: &mut Errors, + tokens: &TokenStream, + lang: Lang, + mut ns: Namespace, +) -> Result { // type Alias = crate::path::to::Type; let parse = |input: ParseStream| -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -416,12 +447,21 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - let doc = attrs::parse_doc(cx, &attrs); + let mut doc = Doc::new(); + attrs::parse( + cx, + &attrs, + attrs::Parser { + doc: Some(&mut doc), + namespace: Some(&mut ns), + ..Default::default() + }, + ); Ok(TypeAlias { doc, type_token, - ident, + ident: Pair::new(ns, ident), eq_token, ty, semi_token, @@ -440,7 +480,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } -fn parse_impl(imp: ItemImpl) -> Result { +fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span); @@ -466,7 +506,7 @@ fn parse_impl(imp: ItemImpl) -> Result { Ok(Api::Impl(Impl { impl_token: imp.impl_token, - ty: parse_type(&self_ty)?, + ty: parse_type(&self_ty, ns)?, brace_token: imp.brace_token, })) } @@ -515,21 +555,21 @@ fn parse_include(input: ParseStream) -> Result { Err(input.error("expected \"quoted/path/to\" or ")) } -fn parse_type(ty: &RustType) -> Result { +fn parse_type(ty: &RustType, ns: &Namespace) -> Result { match ty { - RustType::Reference(ty) => parse_type_reference(ty), - RustType::Path(ty) => parse_type_path(ty), - RustType::Slice(ty) => parse_type_slice(ty), - RustType::BareFn(ty) => parse_type_fn(ty), + RustType::Reference(ty) => parse_type_reference(ty, ns), + RustType::Path(ty) => parse_type_path(ty, ns), + RustType::Slice(ty) => parse_type_slice(ty, ns), + RustType::BareFn(ty) => parse_type_fn(ty, ns), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_reference(ty: &TypeReference) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; let which = match &inner { - Type::Ident(ident) if ident == "str" => { + Type::Ident(ident) if ident.rust == "str" => { if ty.mutability.is_some() { return Err(Error::new_spanned(ty, "unsupported type")); } else { @@ -537,7 +577,7 @@ fn parse_type_reference(ty: &TypeReference) -> Result { } } Type::Slice(slice) => match &slice.inner { - Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8, + Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8, _ => Type::Ref, }, _ => Type::Ref, @@ -550,19 +590,20 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }))) } -fn parse_type_path(ty: &TypePath) -> Result { +fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); + let maybe_resolved_ident = ResolvableName::new(ident.clone()); match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(ident)), + PathArguments::None => return Ok(Type::Ident(maybe_resolved_ident)), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::UniquePtr(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -570,9 +611,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::CxxVector(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -580,9 +621,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustBox(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -590,9 +631,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustVec(Box::new(Ty1 { - name: ident, + name: maybe_resolved_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -606,15 +647,15 @@ fn parse_type_path(ty: &TypePath) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } -fn parse_type_slice(ty: &TypeSlice) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_slice(ty: &TypeSlice, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; Ok(Type::Slice(Box::new(Slice { bracket: ty.bracket_token, inner, }))) } -fn parse_type_fn(ty: &TypeBareFn) -> Result { +fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, @@ -632,7 +673,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; let ident = match &arg.name { Some(ident) => ident.0.clone(), None => format_ident!("_{}", i), @@ -641,7 +682,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { }) .collect::>()?; let mut throws_tokens = None; - let ret = parse_return_type(&ty.output, &mut throws_tokens)?; + let ret = parse_return_type(&ty.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { unsafety: ty.unsafety, @@ -658,6 +699,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, + ns: &Namespace, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), @@ -679,7 +721,7 @@ fn parse_return_type( } } } - match parse_type(ret)? { + match parse_type(ret, ns)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } diff --git a/syntax/qualified.rs b/syntax/qualified.rs index be9bceb..5eefb8d 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -10,6 +10,7 @@ impl QualifiedName { pub fn parse_unquoted(input: ParseStream) -> Result { let mut segments = Vec::new(); let mut trailing_punct = true; + input.parse::>()?; while trailing_punct && input.peek(Ident::peek_any) { let ident = Ident::parse_any(input)?; segments.push(ident); diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 1e5b513..0b79d5f 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,4 +1,5 @@ use crate::syntax::namespace::Namespace; +use crate::syntax::CppName; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -19,12 +20,6 @@ impl ToTokens for Symbol { } } -impl From<&Ident> for Symbol { - fn from(ident: &Ident) -> Self { - Symbol(ident.to_string()) - } -} - impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -34,18 +29,47 @@ impl Symbol { self.0.write_fmt(format_args!("{}", segment)).unwrap(); assert!(self.0.len() > len_before); } + + pub fn from_idents<'a, T: Iterator>(it: T) -> Self { + let mut symbol = Symbol(String::new()); + for segment in it { + segment.write(&mut symbol); + } + assert!(!symbol.0.is_empty()); + symbol + } + + /// For example, for taking a symbol and then making a new symbol + /// for a vec of that symbol. + pub fn prefix_with(&self, prefix: &str) -> Symbol { + Symbol(format!("{}{}", prefix, self.to_string())) + } +} + +pub trait Segment { + fn write(&self, symbol: &mut Symbol); } -pub trait Segment: Display { +impl Segment for str { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for usize { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Ident { + fn write(&self, symbol: &mut Symbol) { + symbol.push(&self); + } +} +impl Segment for Symbol { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } - -impl Segment for str {} -impl Segment for usize {} -impl Segment for Ident {} -impl Segment for Symbol {} impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { @@ -55,9 +79,16 @@ impl Segment for Namespace { } } +impl Segment for CppName { + fn write(&self, symbol: &mut Symbol) { + self.ns.write(symbol); + self.ident.write(symbol); + } +} + impl Segment for &'_ T where - T: ?Sized + Segment, + T: ?Sized + Segment + Display, { fn write(&self, symbol: &mut Symbol) { (**self).write(symbol); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 7618e99..57db8eb 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, Signature, Slice, Struct, Ty1, - Type, TypeAlias, Var, + Atom, Derive, Enum, ExternFn, ExternType, Impl, Pair, Receiver, Ref, ResolvableName, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -11,11 +11,11 @@ impl ToTokens for Type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Type::Ident(ident) => { - if ident == CxxString { - let span = ident.span(); + if ident.rust == CxxString { + let span = ident.rust.span(); tokens.extend(quote_spanned!(span=> ::cxx::)); } - ident.to_tokens(tokens); + ident.rust.to_tokens(tokens); } Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => { ty.to_tokens(tokens) @@ -39,7 +39,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.name.span(); - let name = self.name.to_string(); + let name = self.name.rust.to_string(); if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); } else if name == "Vec" { @@ -121,6 +121,12 @@ impl ToTokens for ExternFn { } } +impl ToTokens for Pair { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.rust.to_tokens(tokens); + } +} + impl ToTokens for Impl { fn to_tokens(&self, tokens: &mut TokenStream) { self.impl_token.to_tokens(tokens); @@ -149,6 +155,12 @@ impl ToTokens for Signature { } } +impl ToTokens for ResolvableName { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.rust.to_tokens(tokens); + } +} + pub struct ReceiverType<'a>(&'a Receiver); impl Receiver { diff --git a/syntax/types.rs b/syntax/types.rs index 5bac76e..178da3e 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,10 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; +use crate::syntax::{ + Api, CppName, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type, + TypeAlias, +}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -16,6 +19,7 @@ pub struct Types<'a> { pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, pub explicit_impls: Set<&'a Impl>, + pub resolutions: Map<&'a Ident, &'a CppName>, } impl<'a> Types<'a> { @@ -28,6 +32,7 @@ impl<'a> Types<'a> { let mut aliases = Map::new(); let mut untrusted = Map::new(); let mut explicit_impls = Set::new(); + let mut resolutions = Map::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -50,6 +55,10 @@ impl<'a> Types<'a> { } } + let mut add_resolution = |pair: &'a Pair| { + resolutions.insert(&pair.rust, &pair.cxx); + }; + let mut type_names = UnorderedSet::new(); let mut function_names = UnorderedSet::new(); for api in apis { @@ -62,7 +71,7 @@ impl<'a> Types<'a> { match api { Api::Include(_) => {} Api::Struct(strct) => { - let ident = &strct.ident; + let ident = &strct.ident.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -73,13 +82,14 @@ impl<'a> Types<'a> { // type, then error. duplicate_name(cx, strct, ident); } - structs.insert(ident, strct); + structs.insert(&strct.ident.rust, strct); for field in &strct.fields { visit(&mut all, &field.ty); } + add_resolution(&strct.ident); } Api::Enum(enm) => { - let ident = &enm.ident; + let ident = &enm.ident.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -91,9 +101,10 @@ impl<'a> Types<'a> { duplicate_name(cx, enm, ident); } enums.insert(ident, enm); + add_resolution(&enm.ident); } Api::CxxType(ety) => { - let ident = &ety.ident; + let ident = &ety.ident.rust; if !type_names.insert(ident) && (cxx.contains(ident) || !structs.contains_key(ident) && !enums.contains_key(ident)) @@ -107,13 +118,15 @@ impl<'a> Types<'a> { if !ety.trusted { untrusted.insert(ident, ety); } + add_resolution(&ety.ident); } Api::RustType(ety) => { - let ident = &ety.ident; + let ident = &ety.ident.rust; if !type_names.insert(ident) { duplicate_name(cx, ety, ident); } rust.insert(ident); + add_resolution(&ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { // Note: duplication of the C++ name is fine because C++ has @@ -130,11 +143,12 @@ impl<'a> Types<'a> { } Api::TypeAlias(alias) => { let ident = &alias.ident; - if !type_names.insert(ident) { - duplicate_name(cx, alias, ident); + if !type_names.insert(&ident.rust) { + duplicate_name(cx, alias, &ident.rust); } - cxx.insert(ident); - aliases.insert(ident, alias); + cxx.insert(&ident.rust); + aliases.insert(&ident.rust, alias); + add_resolution(&alias.ident); } Api::Impl(imp) => { visit(&mut all, &imp.ty); @@ -150,8 +164,8 @@ impl<'a> Types<'a> { let mut required_trivial = Map::new(); let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| { if let Type::Ident(ident) = ty { - if cxx.contains(ident) { - required_trivial.entry(ident).or_insert(reason); + if cxx.contains(&ident.rust) { + required_trivial.entry(&ident.rust).or_insert(reason); } } }; @@ -187,16 +201,17 @@ impl<'a> Types<'a> { untrusted, required_trivial, explicit_impls, + resolutions, } } pub fn needs_indirect_abi(&self, ty: &Type) -> bool { match ty { Type::Ident(ident) => { - if let Some(strct) = self.structs.get(ident) { + if let Some(strct) = self.structs.get(&ident.rust) { !self.is_pod(strct) } else { - Atom::from(ident) == Some(RustString) + Atom::from(&ident.rust) == Some(RustString) } } Type::RustVec(_) => true, @@ -212,6 +227,12 @@ impl<'a> Types<'a> { } false } + + pub fn resolve(&self, ident: &ResolvableName) -> &CppName { + self.resolutions + .get(&ident.rust) + .expect("Unable to resolve type") + } } impl<'t, 'a> IntoIterator for &'t Types<'a> { diff --git a/tests/BUCK b/tests/BUCK index 5bbe500..f51be09 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -15,6 +15,7 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", + "ffi/class_in_ns.rs", ], crate = "cxx_test_suite", deps = [ @@ -30,6 +31,7 @@ cxx_library( ":bridge/source", ":extra/source", ":module/source", + ":class_in_ns/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -52,3 +54,8 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) + +rust_cxx_bridge( + name = "class_in_ns", + src = "ffi/class_in_ns.rs", +) diff --git a/tests/BUILD b/tests/BUILD index 57ffab9..a400f47 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -18,6 +18,7 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", + "ffi/class_in_ns.rs", ], deps = [ ":impl", @@ -32,6 +33,7 @@ cc_library( ":bridge/source", ":extra/source", ":module/source", + ":class_in_ns/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -57,3 +59,9 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) + +rust_cxx_bridge( + name = "class_in_ns", + src = "ffi/class_in_ns.rs", + deps = [":impl"], +) \ No newline at end of file diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 4b2cbdf..9bdb711 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "extra.rs", "module.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs", "class_in_ns.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs new file mode 100644 index 0000000..8b50561 --- /dev/null +++ b/tests/ffi/class_in_ns.rs @@ -0,0 +1,21 @@ +// To test receivers on a type in a namespace outide +// the default. cxx::bridge blocks can only have a single +// receiver type, and there can only be one such block per, +// which is why this is outside. + +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi3 { + + extern "C" { + include!("tests/ffi/tests.h"); + + #[namespace (namespace = I)] + type I; + + fn get(self: &I) -> u32; + + #[namespace (namespace = I)] + fn ns_c_return_unique_ptr_ns() -> UniquePtr; + } +} diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index a809ea4..633cf93 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -12,20 +12,44 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} + impl UniquePtr {} + impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); type D = crate::other::D; type E = crate::other::E; + #[namespace (namespace = F)] + type F = crate::other::f::F; + #[namespace (namespace = G)] + type G = crate::other::G; + + #[namespace(namespace = H)] + type H; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); fn c_take_trivial(d: D); + fn c_take_trivial_ns_ptr(g: UniquePtr); + fn c_take_trivial_ns_ref(g: &G); + fn c_take_trivial_ns(g: G); fn c_take_opaque_ptr(e: UniquePtr); fn c_take_opaque_ref(e: &E); + fn c_take_opaque_ns_ptr(e: UniquePtr); + fn c_take_opaque_ns_ref(e: &F); fn c_return_trivial_ptr() -> UniquePtr; fn c_return_trivial() -> D; + fn c_return_trivial_ns_ptr() -> UniquePtr; + fn c_return_trivial_ns() -> G; fn c_return_opaque_ptr() -> UniquePtr; + fn c_return_ns_opaque_ptr() -> UniquePtr; + fn c_return_ns_unique_ptr() -> UniquePtr; + fn c_take_ref_ns_c(h: &H); + + #[namespace (namespace = other)] + fn ns_c_take_trivial(d: D); + #[namespace (namespace = other)] + fn ns_c_return_trivial() -> D; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4078b37..be145f3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,6 +4,7 @@ clippy::trivially_copy_pass_by_ref )] +pub mod class_in_ns; pub mod extra; pub mod module; @@ -25,6 +26,32 @@ mod other { e_str: CxxString, } + pub mod f { + use cxx::kind::Opaque; + use cxx::{type_id, CxxString, ExternType}; + + #[repr(C)] + pub struct F { + e: u64, + e_str: CxxString, + } + + unsafe impl ExternType for F { + type Id = type_id!("F::F"); + type Kind = Opaque; + } + } + + #[repr(C)] + pub struct G { + pub g: u64, + } + + unsafe impl ExternType for G { + type Id = type_id!("G::G"); + type Kind = Trivial; + } + unsafe impl ExternType for D { type Id = type_id!("tests::D"); type Kind = Trivial; @@ -49,6 +76,32 @@ pub mod ffi { CVal, } + #[namespace(namespace = A)] + #[derive(Clone)] + struct AShared { + z: usize, + } + + #[namespace(namespace = A)] + enum AEnum { + AAVal, + ABVal = 2020, + ACVal, + } + + #[namespace(namespace = A::B)] + enum ABEnum { + ABAVal, + ABBVal = 2020, + ABCVal, + } + + #[namespace(namespace = A::B)] + #[derive(Clone)] + struct ABShared { + z: usize, + } + extern "C" { include!("tests/ffi/tests.h"); @@ -78,6 +131,10 @@ pub mod ffi { fn c_return_identity(_: usize) -> usize; fn c_return_sum(_: usize, _: usize) -> usize; fn c_return_enum(n: u16) -> Enum; + fn c_return_ns_ref(shared: &AShared) -> &usize; + fn c_return_nested_ns_ref(shared: &ABShared) -> &usize; + fn c_return_ns_enum(n: u16) -> AEnum; + fn c_return_nested_ns_enum(n: u16) -> ABEnum; fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); @@ -108,6 +165,12 @@ pub mod ffi { fn c_take_callback(callback: fn(String) -> usize); */ fn c_take_enum(e: Enum); + fn c_take_ns_enum(e: AEnum); + fn c_take_nested_ns_enum(e: ABEnum); + fn c_take_ns_shared(shared: AShared); + fn c_take_nested_ns_shared(shared: ABShared); + fn c_take_rust_vec_ns_shared(v: Vec); + fn c_take_rust_vec_nested_ns_shared(v: Vec); fn c_try_return_void() -> Result<()>; fn c_try_return_primitive() -> Result; @@ -137,6 +200,9 @@ pub mod ffi { fn cOverloadedFunction(x: i32) -> String; #[rust_name = "str_overloaded_function"] fn cOverloadedFunction(x: &str) -> String; + + #[namespace (namespace = other)] + fn ns_c_take_ns_shared(shared: AShared); } extern "C" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 983cf95..05bf5fb 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -43,6 +43,10 @@ size_t c_return_primitive() { return 2020; } Shared c_return_shared() { return Shared{2020}; } +::A::AShared c_return_ns_shared() { return ::A::AShared{2020}; } + +::A::B::ABShared c_return_nested_ns_shared() { return ::A::B::ABShared{2020}; } + rust::Box c_return_box() { return rust::Box::from_raw(cxx_test_suite_get_box()); } @@ -51,8 +55,16 @@ std::unique_ptr c_return_unique_ptr() { return std::unique_ptr(new C{2020}); } +std::unique_ptr<::H::H> c_return_ns_unique_ptr() { + return std::unique_ptr<::H::H>(new ::H::H{"hello"}); +} + const size_t &c_return_ref(const Shared &shared) { return shared.z; } +const size_t &c_return_ns_ref(const ::A::AShared &shared) { return shared.z; } + +const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) { return shared.z; } + size_t &c_return_mut(Shared &shared) { return shared.z; } rust::Str c_return_str(const Shared &shared) { @@ -144,6 +156,26 @@ Enum c_return_enum(uint16_t n) { } } +::A::AEnum c_return_ns_enum(uint16_t n) { + if (n <= static_cast(::A::AEnum::AAVal)) { + return ::A::AEnum::AAVal; + } else if (n <= static_cast(::A::AEnum::ABVal)) { + return ::A::AEnum::ABVal; + } else { + return ::A::AEnum::ACVal; + } +} + +::A::B::ABEnum c_return_nested_ns_enum(uint16_t n) { + if (n <= static_cast(::A::B::ABEnum::ABAVal)) { + return ::A::B::ABEnum::ABAVal; + } else if (n <= static_cast(::A::B::ABEnum::ABBVal)) { + return ::A::B::ABEnum::ABBVal; + } else { + return ::A::B::ABEnum::ABCVal; + } +} + void c_take_primitive(size_t n) { if (n == 2020) { cxx_test_suite_set_correct(); @@ -156,6 +188,18 @@ void c_take_shared(Shared shared) { } } +void c_take_ns_shared(::A::AShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} + +void c_take_nested_ns_shared(::A::B::ABShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } +} + void c_take_box(rust::Box r) { if (cxx_test_suite_r_is_correct(&*r)) { cxx_test_suite_set_correct(); @@ -180,6 +224,12 @@ void c_take_ref_c(const C &c) { } } +void c_take_ref_ns_c(const ::H::H &h) { + if (h.h == "hello") { + cxx_test_suite_set_correct(); + } +} + void c_take_str(rust::Str s) { if (std::string(s) == "2020") { cxx_test_suite_set_correct(); @@ -258,6 +308,26 @@ void c_take_rust_vec_shared(rust::Vec v) { } } +void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v) { + uint32_t sum = 0; + for (auto i : v) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + +void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v) { + uint32_t sum = 0; + for (auto i : v) { + sum += i.z; + } + if (sum == 2021) { + cxx_test_suite_set_correct(); + } +} + void c_take_rust_vec_string(rust::Vec v) { (void)v; cxx_test_suite_set_correct(); @@ -326,6 +396,18 @@ void c_take_enum(Enum e) { } } +void c_take_ns_enum(::A::AEnum e) { + if (e == ::A::AEnum::AAVal) { + cxx_test_suite_set_correct(); + } +} + +void c_take_nested_ns_enum(::A::B::ABEnum e) { + if (e == ::A::B::ABEnum::ABAVal) { + cxx_test_suite_set_correct(); + } +} + void c_try_return_void() {} size_t c_try_return_primitive() { return 2020; } @@ -394,24 +476,56 @@ void c_take_trivial_ref(const D& d) { cxx_test_suite_set_correct(); } } + void c_take_trivial(D d) { if (d.d == 30) { cxx_test_suite_set_correct(); } } + +void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g) { + if (g->g == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ns_ref(const ::G::G& g) { + if (g.g == 30) { + cxx_test_suite_set_correct(); + } +} + +void c_take_trivial_ns(::G::G g) { + if (g.g == 30) { + cxx_test_suite_set_correct(); + } +} + void c_take_opaque_ptr(std::unique_ptr e) { if (e->e == 40) { cxx_test_suite_set_correct(); } } +void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f) { + if (f->f == 40) { + cxx_test_suite_set_correct(); + } +} + void c_take_opaque_ref(const E& e) { if (e.e == 40 && e.e_str == "hello") { cxx_test_suite_set_correct(); } } +void c_take_opaque_ns_ref(const ::F::F& f) { + if (f.f == 40 && f.f_str == "hello") { + cxx_test_suite_set_correct(); + } +} + std::unique_ptr c_return_trivial_ptr() { auto d = std::unique_ptr(new D()); d->d = 30; @@ -424,6 +538,18 @@ D c_return_trivial() { return d; } +std::unique_ptr<::G::G> c_return_trivial_ns_ptr() { + auto g = std::unique_ptr<::G::G>(new ::G::G()); + g->g = 30; + return g; +} + +::G::G c_return_trivial_ns() { + ::G::G g; + g.g = 30; + return g; +} + std::unique_ptr c_return_opaque_ptr() { auto e = std::unique_ptr(new E()); e->e = 40; @@ -431,6 +557,13 @@ std::unique_ptr c_return_opaque_ptr() { return e; } +std::unique_ptr<::F::F> c_return_ns_opaque_ptr() { + auto f = std::unique_ptr<::F::F>(new ::F::F()); + f->f = 40; + f->f_str = std::string("hello"); + return f; +} + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -494,3 +627,34 @@ extern "C" const char *cxx_run_test() noexcept { } } // namespace tests + +namespace other { + + void ns_c_take_trivial(::tests::D d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); + } + } + + ::tests::D ns_c_return_trivial() { + ::tests::D d; + d.d = 30; + return d; + } + + void ns_c_take_ns_shared(::A::AShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); + } + } +} // namespace other + +namespace I { + uint32_t I::get() const { + return a; + } + + std::unique_ptr ns_c_return_unique_ptr_ns() { + return std::unique_ptr(new I()); + } +} // namespace I diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index b3f547e..3835660 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -3,6 +3,35 @@ #include #include +namespace A { + struct AShared; + enum class AEnum : uint16_t; + namespace B { + struct ABShared; + enum class ABEnum : uint16_t; + } // namespace B +} // namespace A + +namespace F { + struct F { + uint64_t f; + std::string f_str; + }; +} + +namespace G { + struct G { + uint64_t g; + }; +} + +namespace H { + class H { + public: + std::string h; + }; +} + namespace tests { struct R; @@ -44,9 +73,14 @@ enum COwnedEnum { size_t c_return_primitive(); Shared c_return_shared(); +::A::AShared c_return_ns_shared(); +::A::B::ABShared c_return_nested_ns_shared(); rust::Box c_return_box(); std::unique_ptr c_return_unique_ptr(); +std::unique_ptr<::H::H> c_return_ns_unique_ptr(); const size_t &c_return_ref(const Shared &shared); +const size_t &c_return_ns_ref(const ::A::AShared &shared); +const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared); size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); @@ -66,13 +100,18 @@ rust::Vec c_return_rust_vec_string(); size_t c_return_identity(size_t n); size_t c_return_sum(size_t n1, size_t n2); Enum c_return_enum(uint16_t n); +::A::AEnum c_return_ns_enum(uint16_t n); +::A::B::ABEnum c_return_nested_ns_enum(uint16_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); +void c_take_ns_shared(::A::AShared shared); +void c_take_nested_ns_shared(::A::B::ABShared shared); void c_take_box(rust::Box r); void c_take_unique_ptr(std::unique_ptr c); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); +void c_take_ref_ns_c(const ::H::H &h); void c_take_str(rust::Str s); void c_take_sliceu8(rust::Slice s); void c_take_rust_string(rust::String s); @@ -86,6 +125,8 @@ void c_take_ref_vector(const std::vector &v); void c_take_rust_vec(rust::Vec v); void c_take_rust_vec_index(rust::Vec v); void c_take_rust_vec_shared(rust::Vec v); +void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v); +void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v); void c_take_rust_vec_string(rust::Vec v); void c_take_rust_vec_shared_index(rust::Vec v); void c_take_rust_vec_shared_forward_iterator(rust::Vec v); @@ -98,6 +139,8 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v); void c_take_callback(rust::Fn callback); */ void c_take_enum(Enum e); +void c_take_ns_enum(::A::AEnum e); +void c_take_nested_ns_enum(::A::B::ABEnum e); void c_try_return_void(); size_t c_try_return_primitive(); @@ -115,13 +158,40 @@ const rust::Vec &c_try_return_ref_rust_vec(const C &c); void c_take_trivial_ptr(std::unique_ptr d); void c_take_trivial_ref(const D& d); void c_take_trivial(D d); + +void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g); +void c_take_trivial_ns_ref(const ::G::G& g); +void c_take_trivial_ns(::G::G g); void c_take_opaque_ptr(std::unique_ptr e); +void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f); void c_take_opaque_ref(const E& e); +void c_take_opaque_ns_ref(const ::F::F& f); std::unique_ptr c_return_trivial_ptr(); D c_return_trivial(); +std::unique_ptr<::G::G> c_return_trivial_ns_ptr(); +::G::G c_return_trivial_ns(); std::unique_ptr c_return_opaque_ptr(); +std::unique_ptr<::F::F> c_return_ns_opaque_ptr(); rust::String cOverloadedFunction(int32_t x); rust::String cOverloadedFunction(rust::Str x); } // namespace tests + +namespace other { + void ns_c_take_trivial(::tests::D d); + ::tests::D ns_c_return_trivial(); + void ns_c_take_ns_shared(::A::AShared shared); +} // namespace other + +namespace I { + class I { + private: + uint32_t a; + public: + I() : a(1000) {} + uint32_t get() const; + }; + + std::unique_ptr ns_c_return_unique_ptr_ns(); +} // namespace I diff --git a/tests/test.rs b/tests/test.rs index b92eebf..20b23ac 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,3 +1,4 @@ +use cxx_test_suite::class_in_ns::ffi3; use cxx_test_suite::extra::ffi2; use cxx_test_suite::ffi; use std::cell::Cell; @@ -23,12 +24,17 @@ macro_rules! check { #[test] fn test_c_return() { let shared = ffi::Shared { z: 2020 }; + let ns_shared = ffi::AShared { z: 2020 }; + let nested_ns_shared = ffi::ABShared { z: 2020 }; assert_eq!(2020, ffi::c_return_primitive()); assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, *ffi::c_return_box()); ffi::c_return_unique_ptr(); + ffi2::c_return_ns_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); + assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared)); + assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared)); assert_eq!("2020", ffi::c_return_str(&shared)); assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); @@ -64,6 +70,14 @@ fn test_c_return() { enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr), _ => assert!(false), } + match ffi::c_return_ns_enum(0) { + enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr), + _ => assert!(false), + } + match ffi::c_return_nested_ns_enum(0) { + enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr), + _ => assert!(false), + } } #[test] @@ -85,11 +99,16 @@ fn test_c_try_return() { #[test] fn test_c_take() { let unique_ptr = ffi::c_return_unique_ptr(); + let unique_ptr_ns = ffi2::c_return_ns_unique_ptr(); check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); + check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 })); + check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 })); + check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); + check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns)); check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); @@ -119,7 +138,16 @@ fn test_c_take() { check!(ffi::c_take_ref_rust_vec(&test_vec)); check!(ffi::c_take_ref_rust_vec_index(&test_vec)); check!(ffi::c_take_ref_rust_vec_copy(&test_vec)); + let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }]; + check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec)); + let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }]; + check!(ffi::c_take_rust_vec_nested_ns_shared( + nested_ns_shared_test_vec + )); + check!(ffi::c_take_enum(ffi::Enum::AVal)); + check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal)); + check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal)); } /* @@ -167,6 +195,14 @@ fn test_c_method_calls() { } #[test] +fn test_c_ns_method_calls() { + let unique_ptr = ffi3::ns_c_return_unique_ptr_ns(); + + let old_value = unique_ptr.get(); + assert_eq!(1000, old_value); +} + +#[test] fn test_enum_representations() { assert_eq!(0, ffi::Enum::AVal.repr); assert_eq!(2020, ffi::Enum::BVal.repr); @@ -200,6 +236,15 @@ fn test_extern_trivial() { let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); cxx::UniquePtr::new(ffi2::D { d: 42 }); + let d = ffi2::ns_c_return_trivial(); + check!(ffi2::ns_c_take_trivial(d)); + + let g = ffi2::c_return_trivial_ns(); + check!(ffi2::c_take_trivial_ns_ref(&g)); + check!(ffi2::c_take_trivial_ns(g)); + let g = ffi2::c_return_trivial_ns_ptr(); + check!(ffi2::c_take_trivial_ns_ptr(g)); + cxx::UniquePtr::new(ffi2::G { g: 42 }); } #[test] @@ -207,4 +252,8 @@ fn test_extern_opaque() { let e = ffi2::c_return_opaque_ptr(); check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); check!(ffi2::c_take_opaque_ptr(e)); + + let f = ffi2::c_return_ns_opaque_ptr(); + check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap())); + check!(ffi2::c_take_opaque_ns_ptr(f)); } diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 0a56dd4..a860f3d 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `S` +error: needs a cxx::ExternType impl in order to be used as a field of `::S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C; From 3e5cff4a3d29873e0cadb33f2990e63988e5c045 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 02:48:45 +0000 Subject: [PATCH 1079/2232] Switch to #[namespace = A::B] syntax. Thanks to sbrocket for this parsing code. --- diff --git a/gen/src/file.rs b/gen/src/file.rs index c696407..1b324cb 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -67,6 +67,6 @@ fn parse_args(attr: &Attribute) -> Result { if attr.tokens.is_empty() { Ok(Namespace::none()) } else { - attr.parse_args() + attr.parse_args_with(Namespace::parse_bridge_attr_namespace) } } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 291b03c..4d9b986 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -18,7 +18,7 @@ use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::qualified::QualifiedName; use proc_macro::TokenStream; -use syn::parse::{Parse, ParseStream, Result}; +use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::parse_macro_input; /// `#[cxx::bridge] mod ffi { ... }` @@ -42,7 +42,10 @@ use syn::parse_macro_input; pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; - let namespace = parse_macro_input!(args as Namespace); + let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) { + Ok(ns) => ns, + Err(err) => return err.to_compile_error().into(), + }; let mut ffi = parse_macro_input!(input as Module); ffi.namespace = namespace; diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 25af229..af73b7a 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -132,8 +132,7 @@ fn parse_function_alias_attribute(input: ParseStream) -> Result { } fn parse_namespace_attribute(input: ParseStream) -> Result { - let content; - syn::parenthesized!(content in input); - let namespace = content.parse::()?; + input.parse::()?; + let namespace = input.parse::()?; Ok(namespace) } diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 49b31d1..b4c6716 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -24,19 +24,23 @@ impl Namespace { pub fn iter(&self) -> Iter { self.segments.iter() } + + pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Namespace::none()); + } + + input.parse::()?; + input.parse::()?; + let ns = input.parse::()?; + input.parse::>()?; + Ok(ns) + } } impl Parse for Namespace { fn parse(input: ParseStream) -> Result { - let mut segments = Vec::new(); - if !input.is_empty() { - input.parse::()?; - input.parse::()?; - segments = input - .call(QualifiedName::parse_quoted_or_unquoted)? - .segments; - input.parse::>()?; - } + let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments; Ok(Namespace { segments }) } } diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs index 8b50561..a03da78 100644 --- a/tests/ffi/class_in_ns.rs +++ b/tests/ffi/class_in_ns.rs @@ -10,12 +10,12 @@ pub mod ffi3 { extern "C" { include!("tests/ffi/tests.h"); - #[namespace (namespace = I)] + #[namespace = "I"] type I; fn get(self: &I) -> u32; - #[namespace (namespace = I)] + #[namespace = "I"] fn ns_c_return_unique_ptr_ns() -> UniquePtr; } } diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 633cf93..58700a2 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -20,12 +20,12 @@ pub mod ffi2 { type D = crate::other::D; type E = crate::other::E; - #[namespace (namespace = F)] + #[namespace = "F"] type F = crate::other::f::F; - #[namespace (namespace = G)] + #[namespace = "G"] type G = crate::other::G; - #[namespace(namespace = H)] + #[namespace = "H"] type H; fn c_take_trivial_ptr(d: UniquePtr); @@ -47,9 +47,9 @@ pub mod ffi2 { fn c_return_ns_unique_ptr() -> UniquePtr; fn c_take_ref_ns_c(h: &H); - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_take_trivial(d: D); - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_return_trivial() -> D; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index be145f3..17146ce 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -76,27 +76,27 @@ pub mod ffi { CVal, } - #[namespace(namespace = A)] + #[namespace = "A"] #[derive(Clone)] struct AShared { z: usize, } - #[namespace(namespace = A)] + #[namespace = "A"] enum AEnum { AAVal, ABVal = 2020, ACVal, } - #[namespace(namespace = A::B)] + #[namespace = "A::B"] enum ABEnum { ABAVal, ABBVal = 2020, ABCVal, } - #[namespace(namespace = A::B)] + #[namespace = "A::B"] #[derive(Clone)] struct ABShared { z: usize, @@ -201,7 +201,7 @@ pub mod ffi { #[rust_name = "str_overloaded_function"] fn cOverloadedFunction(x: &str) -> String; - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_take_ns_shared(shared: AShared); } From 71b34be0be6457e9d939a9005f0f246dc7f98218 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 30 2020 03:05:09 +0000 Subject: [PATCH 1080/2232] Merge pull request #380 from adetaylor/namespaces-syntax-update Switch to #[namespace = A::B] syntax. --- diff --git a/gen/src/file.rs b/gen/src/file.rs index c696407..1b324cb 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -67,6 +67,6 @@ fn parse_args(attr: &Attribute) -> Result { if attr.tokens.is_empty() { Ok(Namespace::none()) } else { - attr.parse_args() + attr.parse_args_with(Namespace::parse_bridge_attr_namespace) } } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 291b03c..4d9b986 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -18,7 +18,7 @@ use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::qualified::QualifiedName; use proc_macro::TokenStream; -use syn::parse::{Parse, ParseStream, Result}; +use syn::parse::{Parse, ParseStream, Parser, Result}; use syn::parse_macro_input; /// `#[cxx::bridge] mod ffi { ... }` @@ -42,7 +42,10 @@ use syn::parse_macro_input; pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; - let namespace = parse_macro_input!(args as Namespace); + let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) { + Ok(ns) => ns, + Err(err) => return err.to_compile_error().into(), + }; let mut ffi = parse_macro_input!(input as Module); ffi.namespace = namespace; diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 25af229..af73b7a 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -132,8 +132,7 @@ fn parse_function_alias_attribute(input: ParseStream) -> Result { } fn parse_namespace_attribute(input: ParseStream) -> Result { - let content; - syn::parenthesized!(content in input); - let namespace = content.parse::()?; + input.parse::()?; + let namespace = input.parse::()?; Ok(namespace) } diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 49b31d1..b4c6716 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -24,19 +24,23 @@ impl Namespace { pub fn iter(&self) -> Iter { self.segments.iter() } + + pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Namespace::none()); + } + + input.parse::()?; + input.parse::()?; + let ns = input.parse::()?; + input.parse::>()?; + Ok(ns) + } } impl Parse for Namespace { fn parse(input: ParseStream) -> Result { - let mut segments = Vec::new(); - if !input.is_empty() { - input.parse::()?; - input.parse::()?; - segments = input - .call(QualifiedName::parse_quoted_or_unquoted)? - .segments; - input.parse::>()?; - } + let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments; Ok(Namespace { segments }) } } diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs index 8b50561..a03da78 100644 --- a/tests/ffi/class_in_ns.rs +++ b/tests/ffi/class_in_ns.rs @@ -10,12 +10,12 @@ pub mod ffi3 { extern "C" { include!("tests/ffi/tests.h"); - #[namespace (namespace = I)] + #[namespace = "I"] type I; fn get(self: &I) -> u32; - #[namespace (namespace = I)] + #[namespace = "I"] fn ns_c_return_unique_ptr_ns() -> UniquePtr; } } diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 633cf93..58700a2 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -20,12 +20,12 @@ pub mod ffi2 { type D = crate::other::D; type E = crate::other::E; - #[namespace (namespace = F)] + #[namespace = "F"] type F = crate::other::f::F; - #[namespace (namespace = G)] + #[namespace = "G"] type G = crate::other::G; - #[namespace(namespace = H)] + #[namespace = "H"] type H; fn c_take_trivial_ptr(d: UniquePtr); @@ -47,9 +47,9 @@ pub mod ffi2 { fn c_return_ns_unique_ptr() -> UniquePtr; fn c_take_ref_ns_c(h: &H); - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_take_trivial(d: D); - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_return_trivial() -> D; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index be145f3..17146ce 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -76,27 +76,27 @@ pub mod ffi { CVal, } - #[namespace(namespace = A)] + #[namespace = "A"] #[derive(Clone)] struct AShared { z: usize, } - #[namespace(namespace = A)] + #[namespace = "A"] enum AEnum { AAVal, ABVal = 2020, ACVal, } - #[namespace(namespace = A::B)] + #[namespace = "A::B"] enum ABEnum { ABAVal, ABBVal = 2020, ABCVal, } - #[namespace(namespace = A::B)] + #[namespace = "A::B"] #[derive(Clone)] struct ABShared { z: usize, @@ -201,7 +201,7 @@ pub mod ffi { #[rust_name = "str_overloaded_function"] fn cOverloadedFunction(x: &str) -> String; - #[namespace (namespace = other)] + #[namespace = "other"] fn ns_c_take_ns_shared(shared: AShared); } From 565ddf035eb945b14cc0b3e6dc00fefac6643db9 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 04:12:36 +0000 Subject: [PATCH 1081/2232] Code review comments on namespace work. --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index c54e784..ee7408e 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -2,39 +2,49 @@ use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; -pub(crate) struct NamespaceEntries<'a> { - pub(crate) entries: Vec<&'a Api>, - pub(crate) children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, +pub struct NamespaceEntries<'a> { + entries: Vec<&'a Api>, + children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } -pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { - let api_refs = apis.iter().collect::>(); - sort_by_inner_namespace(api_refs, 0) -} +impl<'a> NamespaceEntries<'a> { + pub fn new(apis: &'a [Api]) -> Self { + let api_refs = apis.iter().collect::>(); + Self::sort_by_inner_namespace(api_refs, 0) + } -fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { - let mut root = NamespaceEntries { - entries: Vec::new(), - children: BTreeMap::new(), - }; - - let mut kids_by_child_ns = BTreeMap::new(); - for api in apis { - if let Some(ns) = api.get_namespace() { - let first_ns_elem = ns.iter().nth(depth); - if let Some(first_ns_elem) = first_ns_elem { - let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); - list.push(api); - continue; - } - } - root.entries.push(api); + pub fn entries(&self) -> &[&'a Api] { + &self.entries } - for (k, v) in kids_by_child_ns.into_iter() { - root.children - .insert(k, sort_by_inner_namespace(v, depth + 1)); + pub fn children(&self) -> impl Iterator { + self.children.iter() } - root + fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: BTreeMap::new(), + }; + + let mut kids_by_child_ns = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } + } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, Self::sort_by_inner_namespace(v, depth + 1)); + } + + root + } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 967d4ff..7e7a9ea 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,4 @@ -use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; +use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -30,7 +30,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFi out.next_section(); - let apis_by_namespace = sort_by_namespace(apis); + let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_contents(&apis_by_namespace, types, opt, header, out); @@ -51,10 +51,10 @@ fn gen_namespace_contents( header: bool, out: &mut OutFile, ) { - let apis = &ns_entries.entries; + let apis = ns_entries.entries(); out.next_section(); - for api in apis { + for api in apis.into_iter() { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), @@ -64,7 +64,7 @@ fn gen_namespace_contents( } let mut methods_for_type = HashMap::new(); - for api in apis { + for api in apis.into_iter() { if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type @@ -134,7 +134,7 @@ fn gen_namespace_contents( out.next_section(); - for (child_ns, child_ns_entries) in &ns_entries.children { + for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); gen_namespace_contents(&child_ns_entries, types, opt, header, out); writeln!(out, "}} // namespace {}", child_ns); diff --git a/syntax/impls.rs b/syntax/impls.rs index 424ad9d..92c804a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -315,10 +315,6 @@ impl ResolvableName { Self { rust: ident } } - pub fn from_pair(pair: Pair) -> Self { - Self { rust: pair.rust } - } - pub fn make_self(span: Span) -> Self { Self { rust: Token![Self](span).into(), @@ -357,9 +353,7 @@ impl CppName { Self { ns, ident } } - fn iter_all_segments( - &self, - ) -> std::iter::Chain, std::iter::Once<&Ident>> { + fn iter_all_segments(&self) -> impl Iterator { self.ns.iter().chain(std::iter::once(&self.ident)) } diff --git a/syntax/parse.rs b/syntax/parse.rs index 7a56ab8..484b101 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -245,7 +245,7 @@ fn parse_foreign_mod( if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { if receiver.ty.is_self() { - receiver.ty = ResolvableName::from_pair(single_type.clone()); + receiver.ty = ResolvableName::new(single_type.rust.clone()); } } } From f2d9d86c3dd9185d7ffb94749a779ec5f33bac6d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 04:21:30 +0000 Subject: [PATCH 1082/2232] Simplify test suite. --- diff --git a/tests/BUCK b/tests/BUCK index f51be09..5bbe500 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -15,7 +15,6 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", - "ffi/class_in_ns.rs", ], crate = "cxx_test_suite", deps = [ @@ -31,7 +30,6 @@ cxx_library( ":bridge/source", ":extra/source", ":module/source", - ":class_in_ns/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -54,8 +52,3 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) - -rust_cxx_bridge( - name = "class_in_ns", - src = "ffi/class_in_ns.rs", -) diff --git a/tests/BUILD b/tests/BUILD index a400f47..57ffab9 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -18,7 +18,6 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", - "ffi/class_in_ns.rs", ], deps = [ ":impl", @@ -33,7 +32,6 @@ cc_library( ":bridge/source", ":extra/source", ":module/source", - ":class_in_ns/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -59,9 +57,3 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) - -rust_cxx_bridge( - name = "class_in_ns", - src = "ffi/class_in_ns.rs", - deps = [":impl"], -) \ No newline at end of file diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 9bdb711..4b2cbdf 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "extra.rs", "module.rs", "class_in_ns.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs deleted file mode 100644 index a03da78..0000000 --- a/tests/ffi/class_in_ns.rs +++ /dev/null @@ -1,21 +0,0 @@ -// To test receivers on a type in a namespace outide -// the default. cxx::bridge blocks can only have a single -// receiver type, and there can only be one such block per, -// which is why this is outside. - -#[rustfmt::skip] -#[cxx::bridge(namespace = tests)] -pub mod ffi3 { - - extern "C" { - include!("tests/ffi/tests.h"); - - #[namespace = "I"] - type I; - - fn get(self: &I) -> u32; - - #[namespace = "I"] - fn ns_c_return_unique_ptr_ns() -> UniquePtr; - } -} diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 58700a2..a11970d 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -51,5 +51,13 @@ pub mod ffi2 { fn ns_c_take_trivial(d: D); #[namespace = "other"] fn ns_c_return_trivial() -> D; + + #[namespace = "I"] + type I; + + fn get(self: &I) -> u32; + + #[namespace = "I"] + fn ns_c_return_unique_ptr_ns() -> UniquePtr; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 17146ce..fee5bcb 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,7 +4,6 @@ clippy::trivially_copy_pass_by_ref )] -pub mod class_in_ns; pub mod extra; pub mod module; diff --git a/tests/test.rs b/tests/test.rs index 20b23ac..b651feb 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,3 @@ -use cxx_test_suite::class_in_ns::ffi3; use cxx_test_suite::extra::ffi2; use cxx_test_suite::ffi; use std::cell::Cell; @@ -196,7 +195,7 @@ fn test_c_method_calls() { #[test] fn test_c_ns_method_calls() { - let unique_ptr = ffi3::ns_c_return_unique_ptr_ns(); + let unique_ptr = ffi2::ns_c_return_unique_ptr_ns(); let old_value = unique_ptr.get(); assert_eq!(1000, old_value); From 0447e96b840fa1a6fd7bc271cdceda64b45bea59 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 04:22:24 +0000 Subject: [PATCH 1083/2232] Revert to older error string. --- diff --git a/syntax/check.rs b/syntax/check.rs index 08e0dfa..ba488e1 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -211,9 +211,7 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident.rust) { let what = match reason { - TrivialReason::StructField(strct) => { - format!("a field of `{}`", strct.ident.cxx.to_fully_qualified()) - } + TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident.rust), TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index a860f3d..0a56dd4 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `::S` +error: needs a cxx::ExternType impl in order to be used as a field of `S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C; From 0f8ab22aee30f30a86073fee792f831de955aff7 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 04:33:49 +0000 Subject: [PATCH 1084/2232] Improving Pair construction. --- diff --git a/syntax/impls.rs b/syntax/impls.rs index 92c804a..f4c5b05 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -300,14 +300,22 @@ impl Borrow for &Impl { impl Pair { /// Use this constructor when the item can't have a different - /// name in Rust and C++. For cases where #[rust_name] and similar - /// attributes can be used, construct the object by hand. + /// name in Rust and C++. pub fn new(ns: Namespace, ident: Ident) -> Self { Self { rust: ident.clone(), cxx: CppName::new(ns, ident), } } + + /// Use this constructor when attributes such as #[rust_name] + /// can be used to potentially give a different name in Rust vs C++. + pub fn new_from_differing_names(ns: Namespace, cxx_ident: Ident, rust_ident: Ident) -> Self { + Self { + rust: rust_ident, + cxx: CppName::new(ns, cxx_ident), + } + } } impl ResolvableName { diff --git a/syntax/parse.rs b/syntax/parse.rs index 484b101..1927554 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,7 +3,7 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, CppName, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; @@ -398,10 +398,11 @@ fn parse_extern_fn( let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; - let ident = Pair { - cxx: CppName::new(ns, cxx_name.unwrap_or(foreign_fn.sig.ident.clone())), - rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), - }; + let ident = Pair::new_from_differing_names( + ns, + cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + rust_name.unwrap_or(foreign_fn.sig.ident.clone()), + ); let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; let api_function = match lang { From 7827d78f293d7c11222d0de7280066901578ec68 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 05:14:34 +0000 Subject: [PATCH 1085/2232] Add tests for namespace sorter. --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index ee7408e..4aacc8f 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -48,3 +48,81 @@ impl<'a> NamespaceEntries<'a> { root } } + +#[cfg(test)] +mod tests { + use super::NamespaceEntries; + use crate::syntax::namespace::Namespace; + use crate::syntax::{Api, Doc, ExternType, Pair}; + use proc_macro2::{Ident, Span}; + use syn::Token; + + #[test] + fn test_ns_entries_sort() { + let entries = vec![ + make_api(None, "C"), + make_api(None, "A"), + make_api(Some("G"), "E"), + make_api(Some("D"), "F"), + make_api(Some("G"), "H"), + make_api(Some("D::K"), "L"), + make_api(Some("D::K"), "M"), + make_api(None, "B"), + make_api(Some("D"), "I"), + make_api(Some("D"), "J"), + ]; + let ns = NamespaceEntries::new(&entries); + let root_entries = ns.entries(); + assert_eq!(root_entries.len(), 3); + assert_ident(root_entries[0], "C"); + assert_ident(root_entries[1], "A"); + assert_ident(root_entries[2], "B"); + let mut kids = ns.children(); + let (d_id, d_nse) = kids.next().unwrap(); + assert_eq!(d_id.to_string(), "D"); + let (g_id, g_nse) = kids.next().unwrap(); + assert_eq!(g_id.to_string(), "G"); + assert!(kids.next().is_none()); + let d_nse_entries = d_nse.entries(); + assert_eq!(d_nse_entries.len(), 3); + assert_ident(d_nse_entries[0], "F"); + assert_ident(d_nse_entries[1], "I"); + assert_ident(d_nse_entries[2], "J"); + let g_nse_entries = g_nse.entries(); + assert_eq!(g_nse_entries.len(), 2); + assert_ident(g_nse_entries[0], "E"); + assert_ident(g_nse_entries[1], "H"); + let mut g_kids = g_nse.children(); + assert!(g_kids.next().is_none()); + let mut d_kids = d_nse.children(); + let (k_id, k_nse) = d_kids.next().unwrap(); + assert_eq!(k_id.to_string(), "K"); + let k_nse_entries = k_nse.entries(); + assert_eq!(k_nse_entries.len(), 2); + assert_ident(k_nse_entries[0], "L"); + assert_ident(k_nse_entries[1], "M"); + } + + fn assert_ident(api: &Api, expected: &str) { + if let Api::CxxType(cxx_type) = api { + assert_eq!(cxx_type.ident.cxx.ident.to_string(), expected); + } else { + unreachable!() + } + } + + fn make_api(ns: Option<&str>, ident: &str) -> Api { + let ns = match ns { + Some(st) => Namespace::from_str(st), + None => Namespace::none(), + }; + let ident = Pair::new(ns, Ident::new(ident, Span::call_site())); + Api::CxxType(ExternType { + doc: Doc::new(), + type_token: Token![type](Span::call_site()), + ident, + semi_token: Token![;](Span::call_site()), + trusted: true, + }) + } +} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index b4c6716..5eb203a 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,4 +1,6 @@ use crate::syntax::qualified::QualifiedName; +#[cfg(test)] +use proc_macro2::Span; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; @@ -36,6 +38,16 @@ impl Namespace { input.parse::>()?; Ok(ns) } + + #[cfg(test)] + pub fn from_str(ns: &str) -> Self { + Namespace { + segments: ns + .split("::") + .map(|x| Ident::new(x, Span::call_site())) + .collect(), + } + } } impl Parse for Namespace { From 451ec9f44b96e59606b55fe99a277f373cb7c429 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Oct 30 2020 05:51:30 +0000 Subject: [PATCH 1086/2232] Clippy fixes. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 7e7a9ea..b2d52bb 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -54,7 +54,7 @@ fn gen_namespace_contents( let apis = ns_entries.entries(); out.next_section(); - for api in apis.into_iter() { + for api in apis.iter() { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), @@ -64,7 +64,7 @@ fn gen_namespace_contents( } let mut methods_for_type = HashMap::new(); - for api in apis.into_iter() { + for api in apis.iter() { if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type From 6791c39ec61cfdc02df52139879f34e2dca36dd1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 30 2020 21:07:14 +0000 Subject: [PATCH 1087/2232] Merge pull request #382 from adetaylor/namespace-code-review Namespace code review --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index c54e784..4aacc8f 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -2,39 +2,127 @@ use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; -pub(crate) struct NamespaceEntries<'a> { - pub(crate) entries: Vec<&'a Api>, - pub(crate) children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, +pub struct NamespaceEntries<'a> { + entries: Vec<&'a Api>, + children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } -pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { - let api_refs = apis.iter().collect::>(); - sort_by_inner_namespace(api_refs, 0) -} +impl<'a> NamespaceEntries<'a> { + pub fn new(apis: &'a [Api]) -> Self { + let api_refs = apis.iter().collect::>(); + Self::sort_by_inner_namespace(api_refs, 0) + } + + pub fn entries(&self) -> &[&'a Api] { + &self.entries + } -fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { - let mut root = NamespaceEntries { - entries: Vec::new(), - children: BTreeMap::new(), - }; - - let mut kids_by_child_ns = BTreeMap::new(); - for api in apis { - if let Some(ns) = api.get_namespace() { - let first_ns_elem = ns.iter().nth(depth); - if let Some(first_ns_elem) = first_ns_elem { - let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); - list.push(api); - continue; + pub fn children(&self) -> impl Iterator { + self.children.iter() + } + + fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: BTreeMap::new(), + }; + + let mut kids_by_child_ns = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, Self::sort_by_inner_namespace(v, depth + 1)); } - root.entries.push(api); + + root + } +} + +#[cfg(test)] +mod tests { + use super::NamespaceEntries; + use crate::syntax::namespace::Namespace; + use crate::syntax::{Api, Doc, ExternType, Pair}; + use proc_macro2::{Ident, Span}; + use syn::Token; + + #[test] + fn test_ns_entries_sort() { + let entries = vec![ + make_api(None, "C"), + make_api(None, "A"), + make_api(Some("G"), "E"), + make_api(Some("D"), "F"), + make_api(Some("G"), "H"), + make_api(Some("D::K"), "L"), + make_api(Some("D::K"), "M"), + make_api(None, "B"), + make_api(Some("D"), "I"), + make_api(Some("D"), "J"), + ]; + let ns = NamespaceEntries::new(&entries); + let root_entries = ns.entries(); + assert_eq!(root_entries.len(), 3); + assert_ident(root_entries[0], "C"); + assert_ident(root_entries[1], "A"); + assert_ident(root_entries[2], "B"); + let mut kids = ns.children(); + let (d_id, d_nse) = kids.next().unwrap(); + assert_eq!(d_id.to_string(), "D"); + let (g_id, g_nse) = kids.next().unwrap(); + assert_eq!(g_id.to_string(), "G"); + assert!(kids.next().is_none()); + let d_nse_entries = d_nse.entries(); + assert_eq!(d_nse_entries.len(), 3); + assert_ident(d_nse_entries[0], "F"); + assert_ident(d_nse_entries[1], "I"); + assert_ident(d_nse_entries[2], "J"); + let g_nse_entries = g_nse.entries(); + assert_eq!(g_nse_entries.len(), 2); + assert_ident(g_nse_entries[0], "E"); + assert_ident(g_nse_entries[1], "H"); + let mut g_kids = g_nse.children(); + assert!(g_kids.next().is_none()); + let mut d_kids = d_nse.children(); + let (k_id, k_nse) = d_kids.next().unwrap(); + assert_eq!(k_id.to_string(), "K"); + let k_nse_entries = k_nse.entries(); + assert_eq!(k_nse_entries.len(), 2); + assert_ident(k_nse_entries[0], "L"); + assert_ident(k_nse_entries[1], "M"); } - for (k, v) in kids_by_child_ns.into_iter() { - root.children - .insert(k, sort_by_inner_namespace(v, depth + 1)); + fn assert_ident(api: &Api, expected: &str) { + if let Api::CxxType(cxx_type) = api { + assert_eq!(cxx_type.ident.cxx.ident.to_string(), expected); + } else { + unreachable!() + } } - root + fn make_api(ns: Option<&str>, ident: &str) -> Api { + let ns = match ns { + Some(st) => Namespace::from_str(st), + None => Namespace::none(), + }; + let ident = Pair::new(ns, Ident::new(ident, Span::call_site())); + Api::CxxType(ExternType { + doc: Doc::new(), + type_token: Token![type](Span::call_site()), + ident, + semi_token: Token![;](Span::call_site()), + trusted: true, + }) + } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 967d4ff..b2d52bb 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,4 @@ -use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; +use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -30,7 +30,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFi out.next_section(); - let apis_by_namespace = sort_by_namespace(apis); + let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_contents(&apis_by_namespace, types, opt, header, out); @@ -51,10 +51,10 @@ fn gen_namespace_contents( header: bool, out: &mut OutFile, ) { - let apis = &ns_entries.entries; + let apis = ns_entries.entries(); out.next_section(); - for api in apis { + for api in apis.iter() { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), @@ -64,7 +64,7 @@ fn gen_namespace_contents( } let mut methods_for_type = HashMap::new(); - for api in apis { + for api in apis.iter() { if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type @@ -134,7 +134,7 @@ fn gen_namespace_contents( out.next_section(); - for (child_ns, child_ns_entries) in &ns_entries.children { + for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); gen_namespace_contents(&child_ns_entries, types, opt, header, out); writeln!(out, "}} // namespace {}", child_ns); diff --git a/syntax/check.rs b/syntax/check.rs index 08e0dfa..ba488e1 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -211,9 +211,7 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident.rust) { let what = match reason { - TrivialReason::StructField(strct) => { - format!("a field of `{}`", strct.ident.cxx.to_fully_qualified()) - } + TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident.rust), TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; diff --git a/syntax/impls.rs b/syntax/impls.rs index 424ad9d..f4c5b05 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -300,14 +300,22 @@ impl Borrow for &Impl { impl Pair { /// Use this constructor when the item can't have a different - /// name in Rust and C++. For cases where #[rust_name] and similar - /// attributes can be used, construct the object by hand. + /// name in Rust and C++. pub fn new(ns: Namespace, ident: Ident) -> Self { Self { rust: ident.clone(), cxx: CppName::new(ns, ident), } } + + /// Use this constructor when attributes such as #[rust_name] + /// can be used to potentially give a different name in Rust vs C++. + pub fn new_from_differing_names(ns: Namespace, cxx_ident: Ident, rust_ident: Ident) -> Self { + Self { + rust: rust_ident, + cxx: CppName::new(ns, cxx_ident), + } + } } impl ResolvableName { @@ -315,10 +323,6 @@ impl ResolvableName { Self { rust: ident } } - pub fn from_pair(pair: Pair) -> Self { - Self { rust: pair.rust } - } - pub fn make_self(span: Span) -> Self { Self { rust: Token![Self](span).into(), @@ -357,9 +361,7 @@ impl CppName { Self { ns, ident } } - fn iter_all_segments( - &self, - ) -> std::iter::Chain, std::iter::Once<&Ident>> { + fn iter_all_segments(&self) -> impl Iterator { self.ns.iter().chain(std::iter::once(&self.ident)) } diff --git a/syntax/namespace.rs b/syntax/namespace.rs index b4c6716..5eb203a 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,4 +1,6 @@ use crate::syntax::qualified::QualifiedName; +#[cfg(test)] +use proc_macro2::Span; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; @@ -36,6 +38,16 @@ impl Namespace { input.parse::>()?; Ok(ns) } + + #[cfg(test)] + pub fn from_str(ns: &str) -> Self { + Namespace { + segments: ns + .split("::") + .map(|x| Ident::new(x, Span::call_site())) + .collect(), + } + } } impl Parse for Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index 7a56ab8..1927554 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,7 +3,7 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, CppName, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang, Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; @@ -245,7 +245,7 @@ fn parse_foreign_mod( if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { if receiver.ty.is_self() { - receiver.ty = ResolvableName::from_pair(single_type.clone()); + receiver.ty = ResolvableName::new(single_type.rust.clone()); } } } @@ -398,10 +398,11 @@ fn parse_extern_fn( let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; - let ident = Pair { - cxx: CppName::new(ns, cxx_name.unwrap_or(foreign_fn.sig.ident.clone())), - rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), - }; + let ident = Pair::new_from_differing_names( + ns, + cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + rust_name.unwrap_or(foreign_fn.sig.ident.clone()), + ); let paren_token = foreign_fn.sig.paren_token; let semi_token = foreign_fn.semi_token; let api_function = match lang { diff --git a/tests/BUCK b/tests/BUCK index f51be09..5bbe500 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -15,7 +15,6 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", - "ffi/class_in_ns.rs", ], crate = "cxx_test_suite", deps = [ @@ -31,7 +30,6 @@ cxx_library( ":bridge/source", ":extra/source", ":module/source", - ":class_in_ns/source", ], headers = { "ffi/lib.rs.h": ":bridge/header", @@ -54,8 +52,3 @@ rust_cxx_bridge( name = "module", src = "ffi/module.rs", ) - -rust_cxx_bridge( - name = "class_in_ns", - src = "ffi/class_in_ns.rs", -) diff --git a/tests/BUILD b/tests/BUILD index a400f47..57ffab9 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -18,7 +18,6 @@ rust_library( "ffi/extra.rs", "ffi/lib.rs", "ffi/module.rs", - "ffi/class_in_ns.rs", ], deps = [ ":impl", @@ -33,7 +32,6 @@ cc_library( ":bridge/source", ":extra/source", ":module/source", - ":class_in_ns/source", ], hdrs = ["ffi/tests.h"], deps = [ @@ -59,9 +57,3 @@ rust_cxx_bridge( src = "ffi/module.rs", deps = [":impl"], ) - -rust_cxx_bridge( - name = "class_in_ns", - src = "ffi/class_in_ns.rs", - deps = [":impl"], -) \ No newline at end of file diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 9bdb711..4b2cbdf 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -6,7 +6,7 @@ fn main() { } CFG.include_prefix = "tests/ffi"; - let sources = vec!["lib.rs", "extra.rs", "module.rs", "class_in_ns.rs"]; + let sources = vec!["lib.rs", "extra.rs", "module.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/class_in_ns.rs b/tests/ffi/class_in_ns.rs deleted file mode 100644 index a03da78..0000000 --- a/tests/ffi/class_in_ns.rs +++ /dev/null @@ -1,21 +0,0 @@ -// To test receivers on a type in a namespace outide -// the default. cxx::bridge blocks can only have a single -// receiver type, and there can only be one such block per, -// which is why this is outside. - -#[rustfmt::skip] -#[cxx::bridge(namespace = tests)] -pub mod ffi3 { - - extern "C" { - include!("tests/ffi/tests.h"); - - #[namespace = "I"] - type I; - - fn get(self: &I) -> u32; - - #[namespace = "I"] - fn ns_c_return_unique_ptr_ns() -> UniquePtr; - } -} diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index 58700a2..a11970d 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -51,5 +51,13 @@ pub mod ffi2 { fn ns_c_take_trivial(d: D); #[namespace = "other"] fn ns_c_return_trivial() -> D; + + #[namespace = "I"] + type I; + + fn get(self: &I) -> u32; + + #[namespace = "I"] + fn ns_c_return_unique_ptr_ns() -> UniquePtr; } } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 17146ce..fee5bcb 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,7 +4,6 @@ clippy::trivially_copy_pass_by_ref )] -pub mod class_in_ns; pub mod extra; pub mod module; diff --git a/tests/test.rs b/tests/test.rs index 20b23ac..b651feb 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,3 @@ -use cxx_test_suite::class_in_ns::ffi3; use cxx_test_suite::extra::ffi2; use cxx_test_suite::ffi; use std::cell::Cell; @@ -196,7 +195,7 @@ fn test_c_method_calls() { #[test] fn test_c_ns_method_calls() { - let unique_ptr = ffi3::ns_c_return_unique_ptr_ns(); + let unique_ptr = ffi2::ns_c_return_unique_ptr_ns(); let old_value = unique_ptr.get(); assert_eq!(1000, old_value); diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index a860f3d..0a56dd4 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `::S` +error: needs a cxx::ExternType impl in order to be used as a field of `S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C; From 8d32366682c189344565f90f9e8742e167f4b4f9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 03:29:49 +0000 Subject: [PATCH 1088/2232] Match std::string's behavior on (nullptr, 0) construction --- diff --git a/src/cxx.cc b/src/cxx.cc index 715e0ce..cbda8b9 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -57,12 +57,23 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge05$string$drop(this); } -String::String(const std::string &s) : String(s.data(), s.length()) {} +String::String(const std::string &s) { + if (!cxxbridge05$string$from(this, s.data(), s.length())) { + panic("data for rust::String is not utf-8"); + } +} -String::String(const char *s) : String(s, std::strlen(s)) {} +String::String(const char *s) { + if (!cxxbridge05$string$from(this, s, std::strlen(s))) { + panic("data for rust::String is not utf-8"); + } +} String::String(const char *s, size_t len) { - if (!cxxbridge05$string$from(this, s, len)) { + if (!cxxbridge05$string$from( + this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len)) { panic("data for rust::String is not utf-8"); } } @@ -104,15 +115,26 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} +Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : Str(s.data(), s.length()) {} +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { + panic("data for rust::Str is not utf-8"); + } +} -Str::Str(const char *s) : Str(s, std::strlen(s)) {} +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { + if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { + panic("data for rust::Str is not utf-8"); + } +} -Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { +Str::Str(const char *s, size_t len) + : repr( + Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len}) { if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } From 9167590318a1c02c58b8cde13e92f02a6f02d971 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 03:51:26 +0000 Subject: [PATCH 1089/2232] Merge pull request #383 from dtolnay/nullempty Match std::string's behavior on (nullptr, 0) construction --- diff --git a/src/cxx.cc b/src/cxx.cc index 715e0ce..cbda8b9 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -57,12 +57,23 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge05$string$drop(this); } -String::String(const std::string &s) : String(s.data(), s.length()) {} +String::String(const std::string &s) { + if (!cxxbridge05$string$from(this, s.data(), s.length())) { + panic("data for rust::String is not utf-8"); + } +} -String::String(const char *s) : String(s, std::strlen(s)) {} +String::String(const char *s) { + if (!cxxbridge05$string$from(this, s, std::strlen(s))) { + panic("data for rust::String is not utf-8"); + } +} String::String(const char *s, size_t len) { - if (!cxxbridge05$string$from(this, s, len)) { + if (!cxxbridge05$string$from( + this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len)) { panic("data for rust::String is not utf-8"); } } @@ -104,15 +115,26 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -Str::Str() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} +Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : Str(s.data(), s.length()) {} +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { + panic("data for rust::Str is not utf-8"); + } +} -Str::Str(const char *s) : Str(s, std::strlen(s)) {} +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { + if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { + panic("data for rust::Str is not utf-8"); + } +} -Str::Str(const char *s, size_t len) : repr(Repr{s, len}) { +Str::Str(const char *s, size_t len) + : repr( + Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len}) { if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { panic("data for rust::Str is not utf-8"); } From 032d8531611604af1fa246c2e327aff1ac2cc870 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 03:51:51 +0000 Subject: [PATCH 1090/2232] Reduce duplicated checks in string construction --- diff --git a/src/cxx.cc b/src/cxx.cc index cbda8b9..05dc15a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -57,25 +57,20 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge05$string$drop(this); } -String::String(const std::string &s) { - if (!cxxbridge05$string$from(this, s.data(), s.length())) { +static void initString(String *self, const char *s, size_t len) { + if (!cxxbridge05$string$from(self, s, len)) { panic("data for rust::String is not utf-8"); } } -String::String(const char *s) { - if (!cxxbridge05$string$from(this, s, std::strlen(s))) { - panic("data for rust::String is not utf-8"); - } -} +String::String(const std::string &s) { initString(this, s.data(), s.length()); } + +String::String(const char *s) { initString(this, s, std::strlen(s)); } String::String(const char *s, size_t len) { - if (!cxxbridge05$string$from( - this, - s == nullptr && len == 0 ? reinterpret_cast(1) : s, - len)) { - panic("data for rust::String is not utf-8"); - } + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); } String &String::operator=(const String &other) noexcept { @@ -119,25 +114,23 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { +static void initStr(Str::Repr repr) { + if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { panic("data for rust::Str is not utf-8"); } } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + initStr(this->repr); } +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { initStr(this->repr); } + Str::Str(const char *s, size_t len) : repr( Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, len}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } + initStr(this->repr); } Str &Str::operator=(Str other) noexcept { From 6d431e8e24c75cd2a688f959f153ed782b4f85f7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 03:58:36 +0000 Subject: [PATCH 1091/2232] Merge pull request #384 from dtolnay/string Reduce duplicated checks in string construction --- diff --git a/src/cxx.cc b/src/cxx.cc index cbda8b9..05dc15a 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -57,25 +57,20 @@ String::String(String &&other) noexcept { String::~String() noexcept { cxxbridge05$string$drop(this); } -String::String(const std::string &s) { - if (!cxxbridge05$string$from(this, s.data(), s.length())) { +static void initString(String *self, const char *s, size_t len) { + if (!cxxbridge05$string$from(self, s, len)) { panic("data for rust::String is not utf-8"); } } -String::String(const char *s) { - if (!cxxbridge05$string$from(this, s, std::strlen(s))) { - panic("data for rust::String is not utf-8"); - } -} +String::String(const std::string &s) { initString(this, s.data(), s.length()); } + +String::String(const char *s) { initString(this, s, std::strlen(s)); } String::String(const char *s, size_t len) { - if (!cxxbridge05$string$from( - this, - s == nullptr && len == 0 ? reinterpret_cast(1) : s, - len)) { - panic("data for rust::String is not utf-8"); - } + initString(this, + s == nullptr && len == 0 ? reinterpret_cast(1) : s, + len); } String &String::operator=(const String &other) noexcept { @@ -119,25 +114,23 @@ Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} Str::Str(const Str &) noexcept = default; -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { +static void initStr(Str::Repr repr) { + if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { panic("data for rust::Str is not utf-8"); } } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } +Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { + initStr(this->repr); } +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { initStr(this->repr); } + Str::Str(const char *s, size_t len) : repr( Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, len}) { - if (!cxxbridge05$str$valid(this->repr.ptr, this->repr.len)) { - panic("data for rust::Str is not utf-8"); - } + initStr(this->repr); } Str &Str::operator=(Str other) noexcept { From 54b13222e20edfe1977864b9deb947c68a9fc9f7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 03:58:44 +0000 Subject: [PATCH 1092/2232] Add asserts to protect string construction from nullptr when building with assertions --- diff --git a/src/cxx.cc b/src/cxx.cc index 05dc15a..a423df6 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,4 +1,5 @@ #include "../include/cxx.h" +#include #include #include #include @@ -65,9 +66,13 @@ static void initString(String *self, const char *s, size_t len) { String::String(const std::string &s) { initString(this, s.data(), s.length()); } -String::String(const char *s) { initString(this, s, std::strlen(s)); } +String::String(const char *s) { + assert(s != nullptr); + initString(this, s, std::strlen(s)); +} String::String(const char *s, size_t len) { + assert(s != nullptr || len == 0); initString(this, s == nullptr && len == 0 ? reinterpret_cast(1) : s, len); @@ -124,12 +129,16 @@ Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { initStr(this->repr); } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { initStr(this->repr); } +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { + assert(s != nullptr); + initStr(this->repr); +} Str::Str(const char *s, size_t len) : repr( Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, len}) { + assert(s != nullptr || len == 0); initStr(this->repr); } From 2e5c529055a16cc5a34189d90c8b2ddc95a83cfc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:08:17 +0000 Subject: [PATCH 1093/2232] Merge pull request #385 from dtolnay/string Add asserts to protect string construction from nullptr when building with assertions --- diff --git a/src/cxx.cc b/src/cxx.cc index 05dc15a..a423df6 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,4 +1,5 @@ #include "../include/cxx.h" +#include #include #include #include @@ -65,9 +66,13 @@ static void initString(String *self, const char *s, size_t len) { String::String(const std::string &s) { initString(this, s.data(), s.length()); } -String::String(const char *s) { initString(this, s, std::strlen(s)); } +String::String(const char *s) { + assert(s != nullptr); + initString(this, s, std::strlen(s)); +} String::String(const char *s, size_t len) { + assert(s != nullptr || len == 0); initString(this, s == nullptr && len == 0 ? reinterpret_cast(1) : s, len); @@ -124,12 +129,16 @@ Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { initStr(this->repr); } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { initStr(this->repr); } +Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { + assert(s != nullptr); + initStr(this->repr); +} Str::Str(const char *s, size_t len) : repr( Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, len}) { + assert(s != nullptr || len == 0); initStr(this->repr); } From bf23e3e185f1ea93991bd6f366e60e9f389b2f74 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:12:50 +0000 Subject: [PATCH 1094/2232] Format PR 370 with clang-format --- diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 05bf5fb..4abc456 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -2,9 +2,9 @@ #include "tests/ffi/lib.rs.h" #include #include +#include #include #include -#include #include extern "C" void cxx_test_suite_set_correct() noexcept; @@ -63,7 +63,9 @@ const size_t &c_return_ref(const Shared &shared) { return shared.z; } const size_t &c_return_ns_ref(const ::A::AShared &shared) { return shared.z; } -const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) { return shared.z; } +const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) { + return shared.z; +} size_t &c_return_mut(Shared &shared) { return shared.z; } @@ -471,7 +473,7 @@ void c_take_trivial_ptr(std::unique_ptr d) { } } -void c_take_trivial_ref(const D& d) { +void c_take_trivial_ref(const D &d) { if (d.d == 30) { cxx_test_suite_set_correct(); } @@ -483,14 +485,13 @@ void c_take_trivial(D d) { } } - void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g) { if (g->g == 30) { cxx_test_suite_set_correct(); } } -void c_take_trivial_ns_ref(const ::G::G& g) { +void c_take_trivial_ns_ref(const ::G::G &g) { if (g.g == 30) { cxx_test_suite_set_correct(); } @@ -514,13 +515,13 @@ void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f) { } } -void c_take_opaque_ref(const E& e) { +void c_take_opaque_ref(const E &e) { if (e.e == 40 && e.e_str == "hello") { cxx_test_suite_set_correct(); } } -void c_take_opaque_ns_ref(const ::F::F& f) { +void c_take_opaque_ns_ref(const ::F::F &f) { if (f.f == 40 && f.f_str == "hello") { cxx_test_suite_set_correct(); } @@ -629,32 +630,29 @@ extern "C" const char *cxx_run_test() noexcept { } // namespace tests namespace other { - - void ns_c_take_trivial(::tests::D d) { - if (d.d == 30) { - cxx_test_suite_set_correct(); - } +void ns_c_take_trivial(::tests::D d) { + if (d.d == 30) { + cxx_test_suite_set_correct(); } +} - ::tests::D ns_c_return_trivial() { - ::tests::D d; - d.d = 30; - return d; - } +::tests::D ns_c_return_trivial() { + ::tests::D d; + d.d = 30; + return d; +} - void ns_c_take_ns_shared(::A::AShared shared) { - if (shared.z == 2020) { - cxx_test_suite_set_correct(); - } +void ns_c_take_ns_shared(::A::AShared shared) { + if (shared.z == 2020) { + cxx_test_suite_set_correct(); } +} } // namespace other namespace I { - uint32_t I::get() const { - return a; - } +uint32_t I::get() const { return a; } - std::unique_ptr ns_c_return_unique_ptr_ns() { - return std::unique_ptr(new I()); - } +std::unique_ptr ns_c_return_unique_ptr_ns() { + return std::unique_ptr(new I()); +} } // namespace I diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 3835660..6b1ec1e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -4,33 +4,33 @@ #include namespace A { - struct AShared; - enum class AEnum : uint16_t; - namespace B { - struct ABShared; - enum class ABEnum : uint16_t; - } // namespace B +struct AShared; +enum class AEnum : uint16_t; +namespace B { +struct ABShared; +enum class ABEnum : uint16_t; +} // namespace B } // namespace A namespace F { - struct F { - uint64_t f; - std::string f_str; - }; -} +struct F { + uint64_t f; + std::string f_str; +}; +} // namespace F namespace G { - struct G { - uint64_t g; - }; -} +struct G { + uint64_t g; +}; +} // namespace G namespace H { - class H { - public: - std::string h; - }; -} +class H { +public: + std::string h; +}; +} // namespace H namespace tests { @@ -156,16 +156,16 @@ rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); void c_take_trivial_ptr(std::unique_ptr d); -void c_take_trivial_ref(const D& d); +void c_take_trivial_ref(const D &d); void c_take_trivial(D d); void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g); -void c_take_trivial_ns_ref(const ::G::G& g); +void c_take_trivial_ns_ref(const ::G::G &g); void c_take_trivial_ns(::G::G g); void c_take_opaque_ptr(std::unique_ptr e); void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f); -void c_take_opaque_ref(const E& e); -void c_take_opaque_ns_ref(const ::F::F& f); +void c_take_opaque_ref(const E &e); +void c_take_opaque_ns_ref(const ::F::F &f); std::unique_ptr c_return_trivial_ptr(); D c_return_trivial(); std::unique_ptr<::G::G> c_return_trivial_ns_ptr(); @@ -179,19 +179,20 @@ rust::String cOverloadedFunction(rust::Str x); } // namespace tests namespace other { - void ns_c_take_trivial(::tests::D d); - ::tests::D ns_c_return_trivial(); - void ns_c_take_ns_shared(::A::AShared shared); +void ns_c_take_trivial(::tests::D d); +::tests::D ns_c_return_trivial(); +void ns_c_take_ns_shared(::A::AShared shared); } // namespace other namespace I { - class I { - private: - uint32_t a; - public: - I() : a(1000) {} - uint32_t get() const; - }; - - std::unique_ptr ns_c_return_unique_ptr_ns(); +class I { +private: + uint32_t a; + +public: + I() : a(1000) {} + uint32_t get() const; +}; + +std::unique_ptr ns_c_return_unique_ptr_ns(); } // namespace I From 50de2c4813985d3498a6af72046ac3f74fd773d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:16:05 +0000 Subject: [PATCH 1095/2232] Remove various Ident type aliases I don't see these being particularly helpful to maintaining correctness. Where needed, we should clarify the meaning of a field with a comment rather than writing its type as an alias. --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 6498d74..d770d49 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -35,19 +35,6 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; -/// A Rust identifier will forver == a proc_macro2::Ident, -/// but for completeness here's a type alias. -pub type RsIdent = Ident; - -/// At the moment, a Rust name is simply a proc_macro2::Ident. -/// In the future, it may become namespaced based on a mod path. -pub type RsName = RsIdent; - -/// At the moment, a C++ identifier is also a proc_macro2::Ident. -/// In the future, we may wish to make a newtype wrapper here -/// to avoid confusion between C++ and Rust identifiers. -pub type CppIdent = Ident; - #[derive(Clone)] /// A C++ identifier in a particular namespace. /// It is intentional that this does not impl Display, @@ -55,7 +42,7 @@ pub type CppIdent = Ident; /// it as a qualified name or as an unqualfiied name. pub struct CppName { pub ns: Namespace, - pub ident: CppIdent, + pub ident: Ident, } pub enum Api { @@ -117,7 +104,7 @@ pub struct Enum { #[derive(Clone)] pub struct Pair { pub cxx: CppName, - pub rust: RsName, + pub rust: Ident, } pub struct ExternFn { @@ -156,7 +143,7 @@ pub struct Signature { #[derive(Eq, PartialEq, Hash)] pub struct Var { - pub ident: RsIdent, // fields and variables are not namespaced + pub ident: Ident, pub ty: Type, } @@ -170,7 +157,7 @@ pub struct Receiver { } pub struct Variant { - pub ident: RsIdent, + pub ident: Ident, pub discriminant: Discriminant, pub expr: Option, } @@ -218,5 +205,5 @@ pub enum Lang { /// before it can be printed in C++. #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct ResolvableName { - pub rust: RsName, + pub rust: Ident, } From 9071c264e2d7864afbb5ac7498bd6d6502c7dfd4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:18:54 +0000 Subject: [PATCH 1096/2232] Collect name-related structs to the bottom of syntax tree definition --- diff --git a/syntax/mod.rs b/syntax/mod.rs index d770d49..12d8178 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -35,16 +35,6 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; -#[derive(Clone)] -/// A C++ identifier in a particular namespace. -/// It is intentional that this does not impl Display, -/// because we want to force users actively to decide whether to output -/// it as a qualified name or as an unqualfiied name. -pub struct CppName { - pub ns: Namespace, - pub ident: Ident, -} - pub enum Api { Include(Include), Struct(Struct), @@ -99,14 +89,6 @@ pub struct Enum { pub repr: Atom, } -/// A type with a defined Rust name and a fully resolved, -/// qualified, namespaced, C++ name. -#[derive(Clone)] -pub struct Pair { - pub cxx: CppName, - pub rust: Ident, -} - pub struct ExternFn { pub lang: Lang, pub doc: Doc, @@ -201,6 +183,24 @@ pub enum Lang { Rust, } +/// A type with a defined Rust name and a fully resolved, +/// qualified, namespaced, C++ name. +#[derive(Clone)] +pub struct Pair { + pub cxx: CppName, + pub rust: Ident, +} + +#[derive(Clone)] +/// A C++ identifier in a particular namespace. +/// It is intentional that this does not impl Display, +/// because we want to force users actively to decide whether to output +/// it as a qualified name or as an unqualfiied name. +pub struct CppName { + pub ns: Namespace, + pub ident: Ident, +} + /// Wrapper for a type which needs to be resolved /// before it can be printed in C++. #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] From abff2d5ed04414d733c2ba4dfcd67b0c022dba5c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:20:07 +0000 Subject: [PATCH 1097/2232] Rewrap name struct comments to 80 columns --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 12d8178..4f9815d 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -183,26 +183,25 @@ pub enum Lang { Rust, } -/// A type with a defined Rust name and a fully resolved, -/// qualified, namespaced, C++ name. +// A type with a defined Rust name and a fully resolved, qualified, namespaced, +// C++ name. #[derive(Clone)] pub struct Pair { pub cxx: CppName, pub rust: Ident, } +// A C++ identifier in a particular namespace. It is intentional that this does +// not impl Display, because we want to force users actively to decide whether +// to output it as a qualified name or as an unqualfiied name. #[derive(Clone)] -/// A C++ identifier in a particular namespace. -/// It is intentional that this does not impl Display, -/// because we want to force users actively to decide whether to output -/// it as a qualified name or as an unqualfiied name. pub struct CppName { pub ns: Namespace, pub ident: Ident, } -/// Wrapper for a type which needs to be resolved -/// before it can be printed in C++. +// Wrapper for a type which needs to be resolved before it can be printed in +// C++. #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct ResolvableName { pub rust: Ident, From be7e30e0c9ed719c379952753b98c009412ee39f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:21:23 +0000 Subject: [PATCH 1098/2232] Clarify name struct comments --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 4f9815d..b1aaa13 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -183,8 +183,8 @@ pub enum Lang { Rust, } -// A type with a defined Rust name and a fully resolved, qualified, namespaced, -// C++ name. +// An association of a defined Rust name with a fully resolved, namespace +// qualified C++ name. #[derive(Clone)] pub struct Pair { pub cxx: CppName, @@ -193,7 +193,7 @@ pub struct Pair { // A C++ identifier in a particular namespace. It is intentional that this does // not impl Display, because we want to force users actively to decide whether -// to output it as a qualified name or as an unqualfiied name. +// to output it as a qualified name or as an unqualfied name. #[derive(Clone)] pub struct CppName { pub ns: Namespace, From 598154250c402828d9ec26bf388fefcc34884954 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:24:05 +0000 Subject: [PATCH 1099/2232] Remove unneeded derives on ResolvableName --- diff --git a/syntax/mod.rs b/syntax/mod.rs index b1aaa13..8d3b8a2 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -202,7 +202,7 @@ pub struct CppName { // Wrapper for a type which needs to be resolved before it can be printed in // C++. -#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, PartialEq, Hash)] pub struct ResolvableName { pub rust: Ident, } From b560a0fabfbafd3a222a140bd76df13b1daf270c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:40:03 +0000 Subject: [PATCH 1100/2232] Make type information accessible through OutFile --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 8a6bd86..94a54e4 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,9 +1,11 @@ use crate::gen::include::Includes; +use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; -pub(crate) struct OutFile { +pub(crate) struct OutFile<'a> { pub header: bool, + pub types: &'a Types<'a>, pub include: Includes, pub front: Content, content: RefCell, @@ -15,10 +17,11 @@ pub struct Content { blocks_pending: Vec<&'static str>, } -impl OutFile { - pub fn new(header: bool) -> Self { +impl<'a> OutFile<'a> { + pub fn new(header: bool, types: &'a Types) -> Self { OutFile { header, + types, include: Includes::new(), front: Content::new(), content: RefCell::new(Content::new()), diff --git a/gen/src/write.rs b/gen/src/write.rs index b2d52bb..3be5e35 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -10,8 +10,8 @@ use crate::syntax::{ use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFile { - let mut out_file = OutFile::new(header); +pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -> OutFile<'a> { + let mut out_file = OutFile::new(header, types); let out = &mut out_file; if header { From a7c2ea10f160bde07159d2c6b6b98cec968baf13 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:40:14 +0000 Subject: [PATCH 1101/2232] Avoid passing Types around everywhere --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3be5e35..f259222 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -25,8 +25,8 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - } } - write_includes(out, types); - write_include_cxxbridge(out, apis, types); + write_includes(out); + write_include_cxxbridge(out, apis); out.next_section(); @@ -36,7 +36,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - if !header { out.next_section(); - write_generic_instantiations(out, types); + write_generic_instantiations(out); } write!(out.front, "{}", out.include); @@ -80,7 +80,7 @@ fn gen_namespace_contents( Api::Struct(strct) => { out.next_section(); if !types.cxx.contains(&strct.ident.rust) { - write_struct(out, strct, types); + write_struct(out, strct); } } Api::Enum(enm) => { @@ -94,7 +94,7 @@ fn gen_namespace_contents( Api::RustType(ety) => { if let Some(methods) = methods_for_type.get(&ety.ident.rust) { out.next_section(); - write_struct_with_methods(out, ety, methods, types); + write_struct_with_methods(out, ety, methods); } } _ => {} @@ -114,13 +114,13 @@ fn gen_namespace_contents( out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { - let (efn, write): (_, fn(_, _, _, _)) = match api { + let (efn, write): (_, fn(_, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), Api::RustFunction(efn) => (efn, write_rust_function_decl), _ => continue, }; out.next_section(); - write(out, efn, types, &opt.cxx_impl_annotations); + write(out, efn, &opt.cxx_impl_annotations); } out.end_block("extern \"C\""); } @@ -128,7 +128,7 @@ fn gen_namespace_contents( for api in apis { if let Api::RustFunction(efn) = api { out.next_section(); - write_rust_function_shim(out, efn, types); + write_rust_function_shim(out, efn); } } @@ -141,8 +141,8 @@ fn gen_namespace_contents( } } -fn write_includes(out: &mut OutFile, types: &Types) { - for ty in types { +fn write_includes(out: &mut OutFile) { + for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) @@ -160,7 +160,7 @@ fn write_includes(out: &mut OutFile, types: &Types) { } } -fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { +fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { let mut needs_panic = false; let mut needs_rust_string = false; let mut needs_rust_str = false; @@ -170,7 +170,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { let mut needs_rust_fn = false; let mut needs_rust_isize = false; let mut needs_unsafe_bitcopy = false; - for ty in types { + for ty in out.types { match ty { Type::RustBox(_) => { out.include.new = true; @@ -240,13 +240,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { needs_maybe_uninit = true; } for arg in &efn.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { needs_manually_drop = true; break; } } if let Some(ret) = &efn.ret { - if types.needs_indirect_abi(ret) { + if out.types.needs_indirect_abi(ret) { needs_maybe_uninit = true; } } @@ -343,7 +343,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { out.end_block("namespace rust"); } -fn write_struct(out: &mut OutFile, strct: &Struct, types: &Types) { +fn write_struct(out: &mut OutFile, strct: &Struct) { let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -353,7 +353,7 @@ fn write_struct(out: &mut OutFile, strct: &Struct, types: &Types) { writeln!(out, "struct {} final {{", strct.ident.cxx.ident); for field in &strct.fields { write!(out, " "); - write_type_space(out, &field.ty, types); + write_type_space(out, &field.ty); writeln!(out, "{};", field.ident); } writeln!(out, "}};"); @@ -373,12 +373,7 @@ fn write_struct_using(out: &mut OutFile, ident: &CppName) { ); } -fn write_struct_with_methods( - out: &mut OutFile, - ety: &ExternType, - methods: &[&ExternFn], - types: &Types, -) { +fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -396,7 +391,7 @@ fn write_struct_with_methods( write!(out, " "); let sig = &method.sig; let local_name = method.ident.cxx.ident.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false, types); + write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } writeln!(out, "}};"); @@ -500,12 +495,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { } } -fn write_cxx_function_shim( - out: &mut OutFile, - efn: &ExternFn, - types: &Types, - impl_annotations: &Option, -) { +fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option) { if !out.header { if let Some(annotation) = impl_annotations { write!(out, "{} ", annotation); @@ -514,9 +504,9 @@ fn write_cxx_function_shim( if efn.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type_space(out, &efn.ret, types); + write_extern_return_type_space(out, &efn.ret); } - let mangled = mangle::extern_fn(efn, types); + let mangled = mangle::extern_fn(efn, out.types); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { if receiver.mutability.is_none() { @@ -525,7 +515,7 @@ fn write_cxx_function_shim( write!( out, "{} &self", - types.resolve(&receiver.ty).to_fully_qualified() + out.types.resolve(&receiver.ty).to_fully_qualified() ); } for (i, arg) in efn.args.iter().enumerate() { @@ -537,25 +527,25 @@ fn write_cxx_function_shim( } else if let Type::RustVec(_) = arg.ty { write!(out, "const "); } - write_extern_arg(out, arg, types); + write_extern_arg(out, arg); } - let indirect_return = indirect_return(efn, types); + let indirect_return = indirect_return(efn, out.types); if indirect_return { if !efn.args.is_empty() || efn.receiver.is_some() { write!(out, ", "); } - write_indirect_return_type_space(out, efn.ret.as_ref().unwrap(), types); + write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); write!(out, "*return$"); } writeln!(out, ") noexcept {{"); write!(out, " "); - write_return_type(out, &efn.ret, types); + write_return_type(out, &efn.ret); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident.rust), Some(receiver) => write!( out, "({}::*{}$)(", - types.resolve(&receiver.ty).to_fully_qualified(), + out.types.resolve(&receiver.ty).to_fully_qualified(), efn.ident.rust ), } @@ -563,7 +553,7 @@ fn write_cxx_function_shim( if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); } write!(out, ")"); if let Some(receiver) = &efn.receiver { @@ -577,7 +567,7 @@ fn write_cxx_function_shim( Some(receiver) => write!( out, "&{}::{}", - types.resolve(&receiver.ty).to_fully_qualified(), + out.types.resolve(&receiver.ty).to_fully_qualified(), efn.ident.cxx.ident ), } @@ -592,7 +582,7 @@ fn write_cxx_function_shim( if indirect_return { out.include.new = true; write!(out, "new (return$) "); - write_indirect_return_type(out, efn.ret.as_ref().unwrap(), types); + write_indirect_return_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); } else if efn.ret.is_some() { write!(out, "return "); @@ -614,10 +604,10 @@ fn write_cxx_function_shim( write!(out, ", "); } if let Type::RustBox(_) = &arg.ty { - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); write!(out, "::from_raw({})", arg.ident); } else if let Type::UniquePtr(_) = &arg.ty { - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); write!(out, "({})", arg.ident); } else if arg.ty == RustString { write!( @@ -626,9 +616,9 @@ fn write_cxx_function_shim( arg.ident, ); } else if let Type::RustVec(_) = arg.ty { - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); - } else if types.needs_indirect_abi(&arg.ty) { + } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); } else { @@ -663,7 +653,7 @@ fn write_cxx_function_shim( for arg in &efn.args { if let Type::Fn(f) = &arg.ty { let var = &arg.ident; - write_function_pointer_trampoline(out, efn, var, f, types); + write_function_pointer_trampoline(out, efn, var, f); } } } @@ -673,35 +663,33 @@ fn write_function_pointer_trampoline( efn: &ExternFn, var: &Ident, f: &Signature, - types: &Types, ) { out.next_section(); - let r_trampoline = mangle::r_trampoline(efn, var, types); + let r_trampoline = mangle::r_trampoline(efn, var, out.types); let indirect_call = true; - write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); + write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(efn, var, types).to_string(); - write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); + let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string(); + write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call); } -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { - let link_name = mangle::extern_fn(efn, types); +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, _: &Option) { + let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; - write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); + write_rust_function_decl_impl(out, &link_name, efn, indirect_call); } fn write_rust_function_decl_impl( out: &mut OutFile, link_name: &Symbol, sig: &Signature, - types: &Types, indirect_call: bool, ) { if sig.throws { write!(out, "::rust::Str::Repr "); } else { - write_extern_return_type_space(out, &sig.ret, types); + write_extern_return_type_space(out, &sig.ret); } write!(out, "{}(", link_name); let mut needs_comma = false; @@ -712,7 +700,7 @@ fn write_rust_function_decl_impl( write!( out, "{} &self", - types.resolve(&receiver.ty).to_fully_qualified() + out.types.resolve(&receiver.ty).to_fully_qualified() ); needs_comma = true; } @@ -720,14 +708,14 @@ fn write_rust_function_decl_impl( if needs_comma { write!(out, ", "); } - write_extern_arg(out, arg, types); + write_extern_arg(out, arg); needs_comma = true; } - if indirect_return(sig, types) { + if indirect_return(sig, out.types) { if needs_comma { write!(out, ", "); } - write_return_type(out, &sig.ret, types); + write_return_type(out, &sig.ret); write!(out, "*return$"); needs_comma = true; } @@ -740,7 +728,7 @@ fn write_rust_function_decl_impl( writeln!(out, ") noexcept;"); } -fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { +fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } @@ -748,13 +736,13 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { None => efn.ident.cxx.ident.to_string(), Some(receiver) => format!( "{}::{}", - types.resolve(&receiver.ty).ident, + out.types.resolve(&receiver.ty).ident, efn.ident.cxx.ident ), }; - let invoke = mangle::extern_fn(efn, types); + let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; - write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); + write_rust_function_shim_impl(out, &local_name, efn, &invoke, indirect_call); } fn write_rust_function_shim_decl( @@ -762,15 +750,14 @@ fn write_rust_function_shim_decl( local_name: &str, sig: &Signature, indirect_call: bool, - types: &Types, ) { - write_return_type(out, &sig.ret, types); + write_return_type(out, &sig.ret); write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } - write_type_space(out, &arg.ty, types); + write_type_space(out, &arg.ty); write!(out, "{}", arg.ident); } if indirect_call { @@ -794,7 +781,6 @@ fn write_rust_function_shim_impl( out: &mut OutFile, local_name: &str, sig: &Signature, - types: &Types, invoke: &Symbol, indirect_call: bool, ) { @@ -802,36 +788,36 @@ fn write_rust_function_shim_impl( // We've already defined this inside the struct. return; } - write_rust_function_shim_decl(out, local_name, sig, indirect_call, types); + write_rust_function_shim_decl(out, local_name, sig, indirect_call); if out.header { writeln!(out, ";"); return; } writeln!(out, " {{"); for arg in &sig.args { - if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) { + if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, " ::rust::ManuallyDrop<"); - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); writeln!(out, "> {}$(::std::move({0}));", arg.ident); } } write!(out, " "); - let indirect_return = indirect_return(sig, types); + let indirect_return = indirect_return(sig, out.types); if indirect_return { write!(out, "::rust::MaybeUninit<"); - write_type(out, sig.ret.as_ref().unwrap(), types); + write_type(out, sig.ret.as_ref().unwrap()); writeln!(out, "> return$;"); write!(out, " "); } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { - write_type(out, ret, types); + write_type(out, ret); write!(out, "::from_raw("); } Type::UniquePtr(_) => { - write_type(out, ret, types); + write_type(out, ret); write!(out, "("); } Type::Ref(_) => write!(out, "*"), @@ -852,7 +838,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::Str(_) => write!(out, "::rust::Str::Repr("), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), - ty if types.needs_indirect_abi(ty) => write!(out, "&"), + ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} } write!(out, "{}", arg.ident); @@ -860,7 +846,7 @@ fn write_rust_function_shim_impl( Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), - ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"), + ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } } @@ -897,10 +883,10 @@ fn write_rust_function_shim_impl( writeln!(out, "}}"); } -fn write_return_type(out: &mut OutFile, ty: &Option, types: &Types) { +fn write_return_type(out: &mut OutFile, ty: &Option) { match ty { None => write!(out, "void "), - Some(ty) => write_type_space(out, ty, types), + Some(ty) => write_type_space(out, ty), } } @@ -910,27 +896,27 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret)) } -fn write_indirect_return_type(out: &mut OutFile, ty: &Type, types: &Types) { +fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { match ty { Type::RustBox(ty) | Type::UniquePtr(ty) => { - write_type_space(out, &ty.inner, types); + write_type_space(out, &ty.inner); write!(out, "*"); } Type::Ref(ty) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner, types); + write_type(out, &ty.inner); write!(out, " *"); } Type::Str(_) => write!(out, "::rust::Str::Repr"), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), - _ => write_type(out, ty, types), + _ => write_type(out, ty), } } -fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type, types: &Types) { - write_indirect_return_type(out, ty, types); +fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { + write_indirect_return_type(out, ty); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "), @@ -938,73 +924,73 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type, types: &Types) } } -fn write_extern_return_type_space(out: &mut OutFile, ty: &Option, types: &Types) { +fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { match ty { Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => { - write_type_space(out, &ty.inner, types); + write_type_space(out, &ty.inner); write!(out, "*"); } Some(Type::Ref(ty)) => { if ty.mutability.is_none() { write!(out, "const "); } - write_type(out, &ty.inner, types); + write_type(out, &ty.inner); write!(out, " *"); } Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), - Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "), - _ => write_return_type(out, ty, types), + Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), + _ => write_return_type(out, ty), } } -fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { +fn write_extern_arg(out: &mut OutFile, arg: &Var) { match &arg.ty { Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { - write_type_space(out, &ty.inner, types); + write_type_space(out, &ty.inner); write!(out, "*"); } Type::Str(_) => write!(out, "::rust::Str::Repr "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), - _ => write_type_space(out, &arg.ty, types), + _ => write_type_space(out, &arg.ty), } - if types.needs_indirect_abi(&arg.ty) { + if out.types.needs_indirect_abi(&arg.ty) { write!(out, "*"); } write!(out, "{}", arg.ident); } -fn write_type(out: &mut OutFile, ty: &Type, types: &Types) { +fn write_type(out: &mut OutFile, ty: &Type) { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { Some(atom) => write_atom(out, atom), - None => write!(out, "{}", types.resolve(ident).to_fully_qualified()), + None => write!(out, "{}", out.types.resolve(ident).to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); - write_type(out, &ty.inner, types); + write_type(out, &ty.inner); write!(out, ">"); } Type::RustVec(ty) => { write!(out, "::rust::Vec<"); - write_type(out, &ty.inner, types); + write_type(out, &ty.inner); write!(out, ">"); } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); - write_type(out, &ptr.inner, types); + write_type(out, &ptr.inner); write!(out, ">"); } Type::CxxVector(ty) => { write!(out, "::std::vector<"); - write_type(out, &ty.inner, types); + write_type(out, &ty.inner); write!(out, ">"); } Type::Ref(r) => { if r.mutability.is_none() { write!(out, "const "); } - write_type(out, &r.inner, types); + write_type(out, &r.inner); write!(out, " &"); } Type::Slice(_) => { @@ -1020,7 +1006,7 @@ fn write_type(out: &mut OutFile, ty: &Type, types: &Types) { Type::Fn(f) => { write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" }); match &f.ret { - Some(ret) => write_type(out, ret, types), + Some(ret) => write_type(out, ret), None => write!(out, "void"), } write!(out, "("); @@ -1028,7 +1014,7 @@ fn write_type(out: &mut OutFile, ty: &Type, types: &Types) { if i > 0 { write!(out, ", "); } - write_type(out, &arg.ty, types); + write_type(out, &arg.ty); } write!(out, ")>"); } @@ -1056,8 +1042,8 @@ fn write_atom(out: &mut OutFile, atom: Atom) { } } -fn write_type_space(out: &mut OutFile, ty: &Type, types: &Types) { - write_type(out, ty, types); +fn write_type_space(out: &mut OutFile, ty: &Type) { + write_type(out, ty); write_space_after_type(out, ty); } @@ -1096,39 +1082,39 @@ fn to_mangled(ty: &Type, types: &Types) -> Symbol { } } -fn write_generic_instantiations(out: &mut OutFile, types: &Types) { +fn write_generic_instantiations(out: &mut OutFile) { out.begin_block("extern \"C\""); - for ty in types { + for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { out.next_section(); - write_rust_box_extern(out, &types.resolve(&inner)); + write_rust_box_extern(out, &out.types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { if Atom::from(&inner.rust).is_none() { out.next_section(); - write_rust_vec_extern(out, inner, types); + write_rust_vec_extern(out, inner); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if Atom::from(&inner.rust).is_none() - && (!types.aliases.contains_key(&inner.rust) - || types.explicit_impls.contains(ty)) + && (!out.types.aliases.contains_key(&inner.rust) + || out.types.explicit_impls.contains(ty)) { out.next_section(); - write_unique_ptr(out, inner, types); + write_unique_ptr(out, inner); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if Atom::from(&inner.rust).is_none() - && (!types.aliases.contains_key(&inner.rust) - || types.explicit_impls.contains(ty)) + && (!out.types.aliases.contains_key(&inner.rust) + || out.types.explicit_impls.contains(ty)) { out.next_section(); - write_cxx_vector(out, ty, inner, types); + write_cxx_vector(out, ty, inner); } } } @@ -1137,15 +1123,15 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); - for ty in types { + for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, &types.resolve(&inner)); + write_rust_box_impl(out, &out.types.resolve(&inner)); } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { if Atom::from(&inner.rust).is_none() { - write_rust_vec_impl(out, inner, types); + write_rust_vec_impl(out, inner); } } } @@ -1173,10 +1159,10 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName, types: &Types) { +fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName) { let element = Type::Ident(element.clone()); - let inner = to_typename(&element, types); - let instance = to_mangled(&element, types); + let inner = to_typename(&element, out.types); + let instance = to_mangled(&element, out.types); writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); @@ -1223,10 +1209,10 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &CppName) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName, types: &Types) { +fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName) { let element = Type::Ident(element.clone()); - let inner = to_typename(&element, types); - let instance = to_mangled(&element, types); + let inner = to_typename(&element, out.types); + let instance = to_mangled(&element, out.types); writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); @@ -1262,24 +1248,24 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName, types: &Type writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName) { let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&ty, types); + let instance = to_mangled(&ty, out.types); writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); - write_unique_ptr_common(out, &ty, types); + write_unique_ptr_common(out, &ty); writeln!(out, "#endif // CXXBRIDGE05_UNIQUE_PTR_{}", instance); } // Shared by UniquePtr and UniquePtr>. -fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { +fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) { out.include.new = true; out.include.utility = true; - let inner = to_typename(ty, types); - let instance = to_mangled(ty, types); + let inner = to_typename(ty, out.types); + let instance = to_mangled(ty, out.types); let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. We can't @@ -1287,7 +1273,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { // bindings for a "new" method anyway. But the Rust code can't be called // for Opaque types because the 'new' method is not implemented. Type::Ident(ident) => { - types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) + out.types.structs.contains_key(&ident.rust) + || out.types.aliases.contains_key(&ident.rust) } _ => false, }; @@ -1352,10 +1339,10 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName) { let element = Type::Ident(element.clone()); - let inner = to_typename(&element, types); - let instance = to_mangled(&element, types); + let inner = to_typename(&element, out.types); + let instance = to_mangled(&element, out.types); writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); @@ -1374,7 +1361,7 @@ fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableNam writeln!(out, " return &s[pos];"); writeln!(out, "}}"); - write_unique_ptr_common(out, vector_ty, types); + write_unique_ptr_common(out, vector_ty); writeln!(out, "#endif // CXXBRIDGE05_VECTOR_{}", instance); } From 5fedc9a359648ee23ad51d298030795267006c16 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 04:46:17 +0000 Subject: [PATCH 1102/2232] Use a less strange return type for NamespaceEntries::children --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index 4aacc8f..525c367 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -17,8 +17,8 @@ impl<'a> NamespaceEntries<'a> { &self.entries } - pub fn children(&self) -> impl Iterator { - self.children.iter() + pub fn children(&self) -> impl Iterator { + self.children.iter().map(|(k, entries)| (*k, entries)) } fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { From cedcde1ddd45c456f94039445e500e1164ca34f8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 18:47:14 +0000 Subject: [PATCH 1103/2232] Fill in missing const on operator Repr --- diff --git a/include/cxx.h b/include/cxx.h index 0373a38..7b332b4 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -81,7 +81,7 @@ public: size_t len; }; Str(Repr) noexcept; - explicit operator Repr() noexcept; + explicit operator Repr() const noexcept; private: Repr repr; @@ -112,7 +112,7 @@ public: size_t len; }; Slice(Repr) noexcept; - explicit operator Repr() noexcept; + explicit operator Repr() const noexcept; private: Repr repr; @@ -348,7 +348,7 @@ template Slice::Slice(Repr repr_) noexcept : repr(repr_) {} template -Slice::operator Repr() noexcept { +Slice::operator Repr() const noexcept { return this->repr; } #endif // CXXBRIDGE05_RUST_SLICE diff --git a/src/cxx.cc b/src/cxx.cc index a423df6..254072e 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -159,7 +159,7 @@ size_t Str::length() const noexcept { return this->repr.len; } Str::Str(Repr repr_) noexcept : repr(repr_) {} -Str::operator Repr() noexcept { return this->repr; } +Str::operator Repr() const noexcept { return this->repr; } std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); From de9a5b12b30518bc9f66d4d86b8f460b960194c9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 19:17:42 +0000 Subject: [PATCH 1104/2232] Guarantee trivial copy constructor and destructor for Str and Slice Repro: const char *ptr(Str str) { return str.repr.ptr; } size_t len(Str str) { return str.repr.len; } Before: ptr(Str): mov rax, qword ptr [rdi] ret len(Str): mov rax, qword ptr [rdi + 8] ret After: ptr(Str): mov rax, rdi ret len(Str): mov rax, rsi ret --- diff --git a/include/cxx.h b/include/cxx.h index 7b332b4..9f0bf46 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -56,8 +56,6 @@ private: class Str final { public: Str() noexcept; - Str(const Str &) noexcept; - Str(const std::string &); Str(const char *); Str(const char *, size_t); @@ -72,6 +70,10 @@ public: size_t size() const noexcept; size_t length() const noexcept; + // Important in order for System V ABI to pass in registers. + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + // Repr is PRIVATE; must not be used other than by our generated code. // // Not necessarily ABI compatible with &str. Codegen will translate to @@ -93,7 +95,6 @@ template class Slice final { public: Slice() noexcept; - Slice(const Slice &) noexcept; Slice(const T *, size_t count) noexcept; Slice &operator=(Slice) noexcept; @@ -102,6 +103,10 @@ public: size_t size() const noexcept; size_t length() const noexcept; + // Important in order for System V ABI to pass in registers. + Slice(const Slice &) noexcept = default; + ~Slice() noexcept = default; + // Repr is PRIVATE; must not be used other than by our generated code. // // At present this class is only used for &[u8] slices. @@ -318,9 +323,6 @@ template Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} template -Slice::Slice(const Slice &) noexcept = default; - -template Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} template diff --git a/src/cxx.cc b/src/cxx.cc index 254072e..96a1861 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -117,8 +117,6 @@ std::ostream &operator<<(std::ostream &os, const String &s) { Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} -Str::Str(const Str &) noexcept = default; - static void initStr(Str::Repr repr) { if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { panic("data for rust::Str is not utf-8"); From a53086ba5104a13295a8f0478e0fcc8c5840a18f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 19:31:50 +0000 Subject: [PATCH 1105/2232] Merge pull request #387 from dtolnay/trivial Guarantee trivial copy constructor and destructor for Str and Slice --- diff --git a/include/cxx.h b/include/cxx.h index 7b332b4..9f0bf46 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -56,8 +56,6 @@ private: class Str final { public: Str() noexcept; - Str(const Str &) noexcept; - Str(const std::string &); Str(const char *); Str(const char *, size_t); @@ -72,6 +70,10 @@ public: size_t size() const noexcept; size_t length() const noexcept; + // Important in order for System V ABI to pass in registers. + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + // Repr is PRIVATE; must not be used other than by our generated code. // // Not necessarily ABI compatible with &str. Codegen will translate to @@ -93,7 +95,6 @@ template class Slice final { public: Slice() noexcept; - Slice(const Slice &) noexcept; Slice(const T *, size_t count) noexcept; Slice &operator=(Slice) noexcept; @@ -102,6 +103,10 @@ public: size_t size() const noexcept; size_t length() const noexcept; + // Important in order for System V ABI to pass in registers. + Slice(const Slice &) noexcept = default; + ~Slice() noexcept = default; + // Repr is PRIVATE; must not be used other than by our generated code. // // At present this class is only used for &[u8] slices. @@ -318,9 +323,6 @@ template Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} template -Slice::Slice(const Slice &) noexcept = default; - -template Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} template diff --git a/src/cxx.cc b/src/cxx.cc index 254072e..96a1861 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -117,8 +117,6 @@ std::ostream &operator<<(std::ostream &os, const String &s) { Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} -Str::Str(const Str &) noexcept = default; - static void initStr(Str::Repr repr) { if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { panic("data for rust::Str is not utf-8"); From 4852122f35be1e5872728a3a1641f58437646e2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 21:59:42 +0000 Subject: [PATCH 1106/2232] Fill in some missing final specifiers --- diff --git a/include/cxx.h b/include/cxx.h index 9f0bf46..bf16dd0 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -78,7 +78,7 @@ public: // // Not necessarily ABI compatible with &str. Codegen will translate to // cxx::rust_str::RustStr which matches this layout. - struct Repr { + struct Repr final { const char *ptr; size_t len; }; @@ -112,7 +112,7 @@ public: // At present this class is only used for &[u8] slices. // Not necessarily ABI compatible with &[u8]. Codegen will translate to // cxx::rust_sliceu8::RustSliceU8 which matches this layout. - struct Repr { + struct Repr final { const T *ptr; size_t len; }; @@ -187,7 +187,7 @@ public: const T &front() const; const T &back() const; - class const_iterator { + class const_iterator final { public: using difference_type = ptrdiff_t; using value_type = typename std::add_const::type; @@ -231,7 +231,7 @@ template class Fn; template -class Fn { +class Fn final { public: Ret operator()(Args... args) const noexcept(!Throws); Fn operator*() const noexcept; @@ -310,7 +310,7 @@ Fn Fn::operator*() const noexcept { #ifndef CXXBRIDGE05_RUST_BITCOPY #define CXXBRIDGE05_RUST_BITCOPY -struct unsafe_bitcopy_t { +struct unsafe_bitcopy_t final { explicit unsafe_bitcopy_t() = default; }; From cc1ae76bd09dc91b7ada120435539c3f60b2b2a4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 22:53:53 +0000 Subject: [PATCH 1107/2232] Remove redundant header branch from write_cxx_function_shim These shims only get emitted in the non-header case in the first place. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index f259222..25f1b0b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -496,10 +496,8 @@ fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { } fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option) { - if !out.header { - if let Some(annotation) = impl_annotations { - write!(out, "{} ", annotation); - } + if let Some(annotation) = impl_annotations { + write!(out, "{} ", annotation); } if efn.throws { write!(out, "::rust::Str::Repr "); From 504cf3cfb84c7df1e59544793113288add4ba36a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 23:09:18 +0000 Subject: [PATCH 1108/2232] Fix unqualified strncpy call --- diff --git a/src/cxx.cc b/src/cxx.cc index 96a1861..d18bec9 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -167,7 +167,7 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { extern "C" { const char *cxxbridge05$error(const char *ptr, size_t len) { char *copy = new char[len]; - strncpy(copy, ptr, len); + std::strncpy(copy, ptr, len); return copy; } } // extern "C" From 5b41479aeb04f41e719c03e60c6d58e30b6ef878 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 23:58:14 +0000 Subject: [PATCH 1109/2232] Fix stray delete in Error move constructor --- diff --git a/src/cxx.cc b/src/cxx.cc index d18bec9..43bf245 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -180,7 +180,6 @@ Error::Error(const Error &other) { } Error::Error(Error &&other) noexcept { - delete[] this->msg.ptr; this->msg = other.msg; other.msg.ptr = nullptr; other.msg.len = 0; From a0c9bc7167891d7522270f19b971b3a25d7ed807 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 23:59:29 +0000 Subject: [PATCH 1110/2232] Decouple Error from Str::Repr It was misleading to use Str (which ordinarily represents borrowed strings) also for owned error messages. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 25f1b0b..8de422d 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -315,6 +315,25 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } + if needs_rust_error { + out.begin_block("namespace repr"); + writeln!(out, "struct PtrLen final {{"); + writeln!(out, " const char *ptr;"); + writeln!(out, " size_t len;"); + writeln!(out, "}};"); + out.end_block("namespace repr"); + + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + writeln!(out, " static Error error(repr::PtrLen ptrlen) noexcept {{"); + writeln!(out, " Error error;"); + writeln!(out, " error.msg = ptrlen.ptr;"); + writeln!(out, " error.len = ptrlen.len;"); + writeln!(out, " return error;"); + writeln!(out, " }}"); + writeln!(out, "}};"); + } + out.end_block("namespace cxxbridge05"); if needs_trycatch { @@ -685,7 +704,7 @@ fn write_rust_function_decl_impl( indirect_call: bool, ) { if sig.throws { - write!(out, "::rust::Str::Repr "); + write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &sig.ret); } @@ -823,7 +842,7 @@ fn write_rust_function_shim_impl( } } if sig.throws { - write!(out, "::rust::Str::Repr error$ = "); + write!(out, "::rust::repr::PtrLen error$ = "); } write!(out, "{}(", invoke); if sig.receiver.is_some() { @@ -871,7 +890,7 @@ fn write_rust_function_shim_impl( writeln!(out, ";"); if sig.throws { writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " throw ::rust::impl::error(error$);"); writeln!(out, " }}"); } if indirect_return { diff --git a/include/cxx.h b/include/cxx.h index bf16dd0..d044111 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -251,12 +251,14 @@ class Error final : public std::exception { public: Error(const Error &); Error(Error &&) noexcept; - Error(Str::Repr) noexcept; ~Error() noexcept; const char *what() const noexcept override; private: - Str::Repr msg; + Error() noexcept = default; + friend class impl; + const char *msg; + size_t len; }; #endif // CXXBRIDGE05_RUST_ERROR diff --git a/src/cxx.cc b/src/cxx.cc index 43bf245..077f8ce 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -172,22 +172,21 @@ const char *cxxbridge05$error(const char *ptr, size_t len) { } } // extern "C" -Error::Error(Str::Repr msg) noexcept : msg(msg) {} - Error::Error(const Error &other) { - this->msg.ptr = cxxbridge05$error(other.msg.ptr, other.msg.len); - this->msg.len = other.msg.len; + this->msg = cxxbridge05$error(other.msg, other.len); + this->len = other.len; } Error::Error(Error &&other) noexcept { this->msg = other.msg; - other.msg.ptr = nullptr; - other.msg.len = 0; + this->len = other.len; + other.msg = nullptr; + other.len = 0; } -Error::~Error() noexcept { delete[] this->msg.ptr; } +Error::~Error() noexcept { delete[] this->msg; } -const char *Error::what() const noexcept { return this->msg.ptr; } +const char *Error::what() const noexcept { return this->msg; } } // namespace cxxbridge05 } // namespace rust From 84ddf9e27f084d7431eea2c6c33f3a39ce4a7d20 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 23:59:29 +0000 Subject: [PATCH 1111/2232] Move "impl" into anonymous namespace --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8de422d..3b404c6 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -281,6 +281,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "struct unsafe_bitcopy_t;"); } + if needs_rust_error { + out.begin_block("namespace"); + writeln!(out, "template "); + writeln!(out, "class impl;"); + out.end_block("namespace"); + } + include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING"); include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR"); include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE"); @@ -316,22 +323,22 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { } if needs_rust_error { - out.begin_block("namespace repr"); - writeln!(out, "struct PtrLen final {{"); - writeln!(out, " const char *ptr;"); - writeln!(out, " size_t len;"); - writeln!(out, "}};"); - out.end_block("namespace repr"); - - writeln!(out, "class impl final {{"); + out.begin_block("namespace"); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); writeln!(out, "public:"); - writeln!(out, " static Error error(repr::PtrLen ptrlen) noexcept {{"); + writeln!(out, " struct Repr final {{"); + writeln!(out, " const char *msg;"); + writeln!(out, " size_t len;"); + writeln!(out, " }};"); + writeln!(out, " static Error error(Repr repr) noexcept {{"); writeln!(out, " Error error;"); - writeln!(out, " error.msg = ptrlen.ptr;"); - writeln!(out, " error.len = ptrlen.len;"); + writeln!(out, " error.msg = repr.msg;"); + writeln!(out, " error.len = repr.len;"); writeln!(out, " return error;"); writeln!(out, " }}"); writeln!(out, "}};"); + out.end_block("namespace"); } out.end_block("namespace cxxbridge05"); @@ -704,7 +711,7 @@ fn write_rust_function_decl_impl( indirect_call: bool, ) { if sig.throws { - write!(out, "::rust::repr::PtrLen "); + write!(out, "::rust::impl<::rust::Error>::Repr "); } else { write_extern_return_type_space(out, &sig.ret); } @@ -842,7 +849,7 @@ fn write_rust_function_shim_impl( } } if sig.throws { - write!(out, "::rust::repr::PtrLen error$ = "); + write!(out, "::rust::impl<::rust::Error>::Repr error$ = "); } write!(out, "{}(", invoke); if sig.receiver.is_some() { @@ -889,8 +896,8 @@ fn write_rust_function_shim_impl( } writeln!(out, ";"); if sig.throws { - writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::impl::error(error$);"); + writeln!(out, " if (error$.msg) {{"); + writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); } if indirect_return { diff --git a/include/cxx.h b/include/cxx.h index d044111..adbc8e6 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -19,6 +19,11 @@ inline namespace cxxbridge05 { struct unsafe_bitcopy_t; +namespace { +template +class impl; +} + #ifndef CXXBRIDGE05_RUST_STRING #define CXXBRIDGE05_RUST_STRING class String final { @@ -256,7 +261,7 @@ public: private: Error() noexcept = default; - friend class impl; + friend impl; const char *msg; size_t len; }; From d68dfa8ee7624ec7859167bcc4eadd89caca1dda Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Oct 31 2020 23:59:29 +0000 Subject: [PATCH 1112/2232] Decouple exception catch from Str::Repr --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3b404c6..16bb7cd 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -322,25 +322,31 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } + out.begin_block("namespace"); + + if needs_trycatch || needs_rust_error { + out.begin_block("namespace repr"); + writeln!(out, "struct PtrLen final {{"); + writeln!(out, " const char *ptr;"); + writeln!(out, " size_t len;"); + writeln!(out, "}};"); + out.end_block("namespace repr"); + } + if needs_rust_error { - out.begin_block("namespace"); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - writeln!(out, " struct Repr final {{"); - writeln!(out, " const char *msg;"); - writeln!(out, " size_t len;"); - writeln!(out, " }};"); - writeln!(out, " static Error error(Repr repr) noexcept {{"); + writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); writeln!(out, " Error error;"); - writeln!(out, " error.msg = repr.msg;"); + writeln!(out, " error.msg = repr.ptr;"); writeln!(out, " error.len = repr.len;"); writeln!(out, " return error;"); writeln!(out, " }}"); writeln!(out, "}};"); - out.end_block("namespace"); } + out.end_block("namespace"); out.end_block("namespace cxxbridge05"); if needs_trycatch { @@ -526,7 +532,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write!(out, "{} ", annotation); } if efn.throws { - write!(out, "::rust::Str::Repr "); + write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &efn.ret); } @@ -598,7 +604,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: writeln!(out, ";"); write!(out, " "); if efn.throws { - writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); write!(out, " "); @@ -711,7 +717,7 @@ fn write_rust_function_decl_impl( indirect_call: bool, ) { if sig.throws { - write!(out, "::rust::impl<::rust::Error>::Repr "); + write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &sig.ret); } @@ -849,7 +855,7 @@ fn write_rust_function_shim_impl( } } if sig.throws { - write!(out, "::rust::impl<::rust::Error>::Repr error$ = "); + write!(out, "::rust::repr::PtrLen error$ = "); } write!(out, "{}(", invoke); if sig.receiver.is_some() { @@ -896,7 +902,7 @@ fn write_rust_function_shim_impl( } writeln!(out, ";"); if sig.throws { - writeln!(out, " if (error$.msg) {{"); + writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); } From 59c5dc28918752409afd1bfdce6dbafeada5880a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:09:02 +0000 Subject: [PATCH 1113/2232] Merge pull request #388 from dtolnay/error Decouple Error from Str::Repr --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 25f1b0b..16bb7cd 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -281,6 +281,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "struct unsafe_bitcopy_t;"); } + if needs_rust_error { + out.begin_block("namespace"); + writeln!(out, "template "); + writeln!(out, "class impl;"); + out.end_block("namespace"); + } + include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING"); include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR"); include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE"); @@ -315,6 +322,31 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } + out.begin_block("namespace"); + + if needs_trycatch || needs_rust_error { + out.begin_block("namespace repr"); + writeln!(out, "struct PtrLen final {{"); + writeln!(out, " const char *ptr;"); + writeln!(out, " size_t len;"); + writeln!(out, "}};"); + out.end_block("namespace repr"); + } + + if needs_rust_error { + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); + writeln!(out, " Error error;"); + writeln!(out, " error.msg = repr.ptr;"); + writeln!(out, " error.len = repr.len;"); + writeln!(out, " return error;"); + writeln!(out, " }}"); + writeln!(out, "}};"); + } + + out.end_block("namespace"); out.end_block("namespace cxxbridge05"); if needs_trycatch { @@ -500,7 +532,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write!(out, "{} ", annotation); } if efn.throws { - write!(out, "::rust::Str::Repr "); + write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &efn.ret); } @@ -572,7 +604,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: writeln!(out, ";"); write!(out, " "); if efn.throws { - writeln!(out, "::rust::Str::Repr throw$;"); + writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); write!(out, " "); @@ -685,7 +717,7 @@ fn write_rust_function_decl_impl( indirect_call: bool, ) { if sig.throws { - write!(out, "::rust::Str::Repr "); + write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &sig.ret); } @@ -823,7 +855,7 @@ fn write_rust_function_shim_impl( } } if sig.throws { - write!(out, "::rust::Str::Repr error$ = "); + write!(out, "::rust::repr::PtrLen error$ = "); } write!(out, "{}(", invoke); if sig.receiver.is_some() { @@ -871,7 +903,7 @@ fn write_rust_function_shim_impl( writeln!(out, ";"); if sig.throws { writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::Error(error$);"); + writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); } if indirect_return { diff --git a/include/cxx.h b/include/cxx.h index bf16dd0..adbc8e6 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -19,6 +19,11 @@ inline namespace cxxbridge05 { struct unsafe_bitcopy_t; +namespace { +template +class impl; +} + #ifndef CXXBRIDGE05_RUST_STRING #define CXXBRIDGE05_RUST_STRING class String final { @@ -251,12 +256,14 @@ class Error final : public std::exception { public: Error(const Error &); Error(Error &&) noexcept; - Error(Str::Repr) noexcept; ~Error() noexcept; const char *what() const noexcept override; private: - Str::Repr msg; + Error() noexcept = default; + friend impl; + const char *msg; + size_t len; }; #endif // CXXBRIDGE05_RUST_ERROR diff --git a/src/cxx.cc b/src/cxx.cc index 43bf245..077f8ce 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -172,22 +172,21 @@ const char *cxxbridge05$error(const char *ptr, size_t len) { } } // extern "C" -Error::Error(Str::Repr msg) noexcept : msg(msg) {} - Error::Error(const Error &other) { - this->msg.ptr = cxxbridge05$error(other.msg.ptr, other.msg.len); - this->msg.len = other.msg.len; + this->msg = cxxbridge05$error(other.msg, other.len); + this->len = other.len; } Error::Error(Error &&other) noexcept { this->msg = other.msg; - other.msg.ptr = nullptr; - other.msg.len = 0; + this->len = other.len; + other.msg = nullptr; + other.len = 0; } -Error::~Error() noexcept { delete[] this->msg.ptr; } +Error::~Error() noexcept { delete[] this->msg; } -const char *Error::what() const noexcept { return this->msg.ptr; } +const char *Error::what() const noexcept { return this->msg; } } // namespace cxxbridge05 } // namespace rust From d5712ee32c6366ee087f19fd72f7b68007a9a821 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:10:00 +0000 Subject: [PATCH 1114/2232] Use member initializer lists for Error constructors --- diff --git a/src/cxx.cc b/src/cxx.cc index 077f8ce..1bfd8b9 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -172,14 +172,10 @@ const char *cxxbridge05$error(const char *ptr, size_t len) { } } // extern "C" -Error::Error(const Error &other) { - this->msg = cxxbridge05$error(other.msg, other.len); - this->len = other.len; -} +Error::Error(const Error &other) + : msg(cxxbridge05$error(other.msg, other.len)), len(other.len) {} -Error::Error(Error &&other) noexcept { - this->msg = other.msg; - this->len = other.len; +Error::Error(Error &&other) noexcept : msg(other.msg), len(other.len) { other.msg = nullptr; other.len = 0; } From 23c2319bc860aab99222cdaaa60ff5ff665fc60f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:11:48 +0000 Subject: [PATCH 1115/2232] Fill in Error base class constructor calls --- diff --git a/src/cxx.cc b/src/cxx.cc index 1bfd8b9..0fba35c 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -173,9 +173,11 @@ const char *cxxbridge05$error(const char *ptr, size_t len) { } // extern "C" Error::Error(const Error &other) - : msg(cxxbridge05$error(other.msg, other.len)), len(other.len) {} + : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)), + len(other.len) {} -Error::Error(Error &&other) noexcept : msg(other.msg), len(other.len) { +Error::Error(Error &&other) noexcept + : std::exception(std::move(other)), msg(other.msg), len(other.len) { other.msg = nullptr; other.len = 0; } From e58f4270ed8c2e2b3a67c5f9cf74636e4641d128 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:22:36 +0000 Subject: [PATCH 1116/2232] Merge pull request #389 from dtolnay/error Fill in Error base class constructor calls --- diff --git a/src/cxx.cc b/src/cxx.cc index 1bfd8b9..0fba35c 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -173,9 +173,11 @@ const char *cxxbridge05$error(const char *ptr, size_t len) { } // extern "C" Error::Error(const Error &other) - : msg(cxxbridge05$error(other.msg, other.len)), len(other.len) {} + : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)), + len(other.len) {} -Error::Error(Error &&other) noexcept : msg(other.msg), len(other.len) { +Error::Error(Error &&other) noexcept + : std::exception(std::move(other)), msg(other.msg), len(other.len) { other.msg = nullptr; other.len = 0; } From 7c6ac7195e06a705011e7d9a96818034f05fe98f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:24:32 +0000 Subject: [PATCH 1117/2232] Add copy assignment operator for Error --- diff --git a/include/cxx.h b/include/cxx.h index adbc8e6..d43854c 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -257,6 +257,9 @@ public: Error(const Error &); Error(Error &&) noexcept; ~Error() noexcept; + + Error &operator=(const Error &); + const char *what() const noexcept override; private: diff --git a/src/cxx.cc b/src/cxx.cc index 0fba35c..7843b29 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -184,6 +184,17 @@ Error::Error(Error &&other) noexcept Error::~Error() noexcept { delete[] this->msg; } +Error &Error::operator=(const Error &other) { + if (this != &other) { + std::exception::operator=(other); + delete[] this->msg; + this->msg = nullptr; + this->msg = cxxbridge05$error(other.msg, other.len); + this->len = other.len; + } + return *this; +} + const char *Error::what() const noexcept { return this->msg; } } // namespace cxxbridge05 From 1549106cb02a216c2182e3c75cfc8aad13834d13 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:26:23 +0000 Subject: [PATCH 1118/2232] Add move assignment operator for Error --- diff --git a/include/cxx.h b/include/cxx.h index d43854c..c545e50 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -259,6 +259,7 @@ public: ~Error() noexcept; Error &operator=(const Error &); + Error &operator=(Error &&) noexcept; const char *what() const noexcept override; diff --git a/src/cxx.cc b/src/cxx.cc index 7843b29..722a034 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -195,6 +195,17 @@ Error &Error::operator=(const Error &other) { return *this; } +Error &Error::operator=(Error &&other) noexcept { + if (this != &other) { + std::exception::operator=(std::move(other)); + this->msg = other.msg; + this->len = other.len; + other.msg = nullptr; + other.len = 0; + } + return *this; +} + const char *Error::what() const noexcept { return this->msg; } } // namespace cxxbridge05 From 5df1f06eb8c1b7d01920c18246fe746f292efb5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:31:44 +0000 Subject: [PATCH 1119/2232] Eliminate Str::Repr struct --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 16bb7cd..10c6b40 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -619,7 +619,6 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), Some(Type::SliceRefU8(_)) if !indirect_return => { write!(out, "::rust::Slice::Repr(") } @@ -659,7 +658,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), + Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -866,7 +865,6 @@ fn write_rust_function_shim_impl( write!(out, ", "); } match &arg.ty { - Type::Str(_) => write!(out, "::rust::Str::Repr("), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -875,7 +873,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), + Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -939,7 +937,6 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, " *"); } - Type::Str(_) => write!(out, "::rust::Str::Repr"), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } @@ -967,7 +964,6 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), @@ -980,7 +976,6 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::rust::Str::Repr "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } diff --git a/include/cxx.h b/include/cxx.h index c545e50..04a63d6 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -79,19 +79,11 @@ public: Str(const Str &) noexcept = default; ~Str() noexcept = default; - // Repr is PRIVATE; must not be used other than by our generated code. - // +private: // Not necessarily ABI compatible with &str. Codegen will translate to // cxx::rust_str::RustStr which matches this layout. - struct Repr final { - const char *ptr; - size_t len; - }; - Str(Repr) noexcept; - explicit operator Repr() const noexcept; - -private: - Repr repr; + const char *ptr; + size_t len; }; #endif // CXXBRIDGE05_RUST_STR diff --git a/src/cxx.cc b/src/cxx.cc index 722a034..7e9f57e 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -115,33 +115,33 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} +Str::Str() noexcept : ptr(reinterpret_cast(1)), len(0) {} -static void initStr(Str::Repr repr) { - if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { +static void initStr(const char *ptr, size_t len) { + if (!cxxbridge05$str$valid(ptr, len)) { panic("data for rust::Str is not utf-8"); } } -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - initStr(this->repr); +Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) { + initStr(this->ptr, this->len); } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { +Str::Str(const char *s) : ptr(s), len(std::strlen(s)) { assert(s != nullptr); - initStr(this->repr); + initStr(this->ptr, this->len); } Str::Str(const char *s, size_t len) - : repr( - Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, - len}) { + : ptr(s == nullptr && len == 0 ? reinterpret_cast(1) : s), + len(len) { assert(s != nullptr || len == 0); - initStr(this->repr); + initStr(this->ptr, this->len); } Str &Str::operator=(Str other) noexcept { - this->repr = other.repr; + this->ptr = other.ptr; + this->len = other.len; return *this; } @@ -149,15 +149,11 @@ Str::operator std::string() const { return std::string(this->data(), this->size()); } -const char *Str::data() const noexcept { return this->repr.ptr; } - -size_t Str::size() const noexcept { return this->repr.len; } - -size_t Str::length() const noexcept { return this->repr.len; } +const char *Str::data() const noexcept { return this->ptr; } -Str::Str(Repr repr_) noexcept : repr(repr_) {} +size_t Str::size() const noexcept { return this->len; } -Str::operator Repr() const noexcept { return this->repr; } +size_t Str::length() const noexcept { return this->len; } std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs index 527d709..3d3bf0f 100644 --- a/tests/cxx_gen.rs +++ b/tests/cxx_gen.rs @@ -18,7 +18,7 @@ fn test_extern_c_function() { let output = str::from_utf8(&generated.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. - assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str foo)")); } #[test] @@ -28,5 +28,5 @@ fn test_impl_annotation() { let source = BRIDGE0.parse().unwrap(); let generated = generate_header_and_cc(source, &opt).unwrap(); let output = str::from_utf8(&generated.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str foo)")); } From 09d2cd9f375513e96c82a87402c3be1dc2df394f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:43:27 +0000 Subject: [PATCH 1120/2232] Merge pull request #391 from dtolnay/str Eliminate Str::Repr struct --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 16bb7cd..10c6b40 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -619,7 +619,6 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("), Some(Type::SliceRefU8(_)) if !indirect_return => { write!(out, "::rust::Slice::Repr(") } @@ -659,7 +658,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), + Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -866,7 +865,6 @@ fn write_rust_function_shim_impl( write!(out, ", "); } match &arg.ty { - Type::Str(_) => write!(out, "::rust::Str::Repr("), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -875,7 +873,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), + Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -939,7 +937,6 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, " *"); } - Type::Str(_) => write!(out, "::rust::Str::Repr"), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } @@ -967,7 +964,6 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), @@ -980,7 +976,6 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::rust::Str::Repr "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } diff --git a/include/cxx.h b/include/cxx.h index c545e50..04a63d6 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -79,19 +79,11 @@ public: Str(const Str &) noexcept = default; ~Str() noexcept = default; - // Repr is PRIVATE; must not be used other than by our generated code. - // +private: // Not necessarily ABI compatible with &str. Codegen will translate to // cxx::rust_str::RustStr which matches this layout. - struct Repr final { - const char *ptr; - size_t len; - }; - Str(Repr) noexcept; - explicit operator Repr() const noexcept; - -private: - Repr repr; + const char *ptr; + size_t len; }; #endif // CXXBRIDGE05_RUST_STR diff --git a/src/cxx.cc b/src/cxx.cc index 722a034..7e9f57e 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -115,33 +115,33 @@ std::ostream &operator<<(std::ostream &os, const String &s) { return os; } -Str::Str() noexcept : repr(Repr{reinterpret_cast(1), 0}) {} +Str::Str() noexcept : ptr(reinterpret_cast(1)), len(0) {} -static void initStr(Str::Repr repr) { - if (!cxxbridge05$str$valid(repr.ptr, repr.len)) { +static void initStr(const char *ptr, size_t len) { + if (!cxxbridge05$str$valid(ptr, len)) { panic("data for rust::Str is not utf-8"); } } -Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) { - initStr(this->repr); +Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) { + initStr(this->ptr, this->len); } -Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) { +Str::Str(const char *s) : ptr(s), len(std::strlen(s)) { assert(s != nullptr); - initStr(this->repr); + initStr(this->ptr, this->len); } Str::Str(const char *s, size_t len) - : repr( - Repr{s == nullptr && len == 0 ? reinterpret_cast(1) : s, - len}) { + : ptr(s == nullptr && len == 0 ? reinterpret_cast(1) : s), + len(len) { assert(s != nullptr || len == 0); - initStr(this->repr); + initStr(this->ptr, this->len); } Str &Str::operator=(Str other) noexcept { - this->repr = other.repr; + this->ptr = other.ptr; + this->len = other.len; return *this; } @@ -149,15 +149,11 @@ Str::operator std::string() const { return std::string(this->data(), this->size()); } -const char *Str::data() const noexcept { return this->repr.ptr; } - -size_t Str::size() const noexcept { return this->repr.len; } - -size_t Str::length() const noexcept { return this->repr.len; } +const char *Str::data() const noexcept { return this->ptr; } -Str::Str(Repr repr_) noexcept : repr(repr_) {} +size_t Str::size() const noexcept { return this->len; } -Str::operator Repr() const noexcept { return this->repr; } +size_t Str::length() const noexcept { return this->len; } std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs index 527d709..3d3bf0f 100644 --- a/tests/cxx_gen.rs +++ b/tests/cxx_gen.rs @@ -18,7 +18,7 @@ fn test_extern_c_function() { let output = str::from_utf8(&generated.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. - assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str foo)")); } #[test] @@ -28,5 +28,5 @@ fn test_impl_annotation() { let source = BRIDGE0.parse().unwrap(); let generated = generate_header_and_cc(source, &opt).unwrap(); let output = str::from_utf8(&generated.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)")); + assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str foo)")); } From 2d7f1174944fade92075e0402d3924371d24a0b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 00:58:31 +0000 Subject: [PATCH 1121/2232] Make Str, Slice trivially copy assignable --- diff --git a/include/cxx.h b/include/cxx.h index 04a63d6..3499fec 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -66,7 +66,7 @@ public: Str(const char *, size_t); Str(std::string &&) = delete; - Str &operator=(Str) noexcept; + Str &operator=(const Str &) noexcept = default; explicit operator std::string() const; @@ -94,7 +94,7 @@ public: Slice() noexcept; Slice(const T *, size_t count) noexcept; - Slice &operator=(Slice) noexcept; + Slice &operator=(const Slice &) noexcept = default; const T *data() const noexcept; size_t size() const noexcept; @@ -329,12 +329,6 @@ template Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} template -Slice &Slice::operator=(Slice other) noexcept { - this->repr = other.repr; - return *this; -} - -template const T *Slice::data() const noexcept { return this->repr.ptr; } diff --git a/src/cxx.cc b/src/cxx.cc index 7e9f57e..d423ab7 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -139,12 +139,6 @@ Str::Str(const char *s, size_t len) initStr(this->ptr, this->len); } -Str &Str::operator=(Str other) noexcept { - this->ptr = other.ptr; - this->len = other.len; - return *this; -} - Str::operator std::string() const { return std::string(this->data(), this->size()); } From 9ed15c6f692d4fc471e128499d8247c1b144242e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 01:02:03 +0000 Subject: [PATCH 1122/2232] Add static assertions for Str trivial operations --- diff --git a/src/cxx.cc b/src/cxx.cc index d423ab7..08ad3ce 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include extern "C" { @@ -154,6 +155,12 @@ std::ostream &operator<<(std::ostream &os, const Str &s) { return os; } +static_assert(std::is_trivially_copy_constructible::value, + "trivial Str(const Str &)"); +static_assert(std::is_trivially_copy_assignable::value, + "trivial operator=(const Str &)"); +static_assert(std::is_trivially_destructible::value, "trivial ~Str()"); + extern "C" { const char *cxxbridge05$error(const char *ptr, size_t len) { char *copy = new char[len]; From 5b1ee1fc4e4606772ab7dce6bbc9011c483072ea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 01:21:39 +0000 Subject: [PATCH 1123/2232] Inline some Str accessors into the header --- diff --git a/include/cxx.h b/include/cxx.h index 3499fec..de614d1 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -57,7 +57,6 @@ private: #endif // CXXBRIDGE05_RUST_STRING #ifndef CXXBRIDGE05_RUST_STR -#define CXXBRIDGE05_RUST_STR class Str final { public: Str() noexcept; @@ -320,6 +319,15 @@ struct unsafe_bitcopy_t final { constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE05_RUST_BITCOPY +#ifndef CXXBRIDGE05_RUST_STR +#define CXXBRIDGE05_RUST_STR +inline const char *Str::data() const noexcept { return this->ptr; } + +inline size_t Str::size() const noexcept { return this->len; } + +inline size_t Str::length() const noexcept { return this->len; } +#endif // CXXBRIDGE05_RUST_STR + #ifndef CXXBRIDGE05_RUST_SLICE #define CXXBRIDGE05_RUST_SLICE template diff --git a/src/cxx.cc b/src/cxx.cc index 08ad3ce..5877b35 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -144,12 +144,6 @@ Str::operator std::string() const { return std::string(this->data(), this->size()); } -const char *Str::data() const noexcept { return this->ptr; } - -size_t Str::size() const noexcept { return this->len; } - -size_t Str::length() const noexcept { return this->len; } - std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); return os; From 54742b7ced559bac4eba72b45ae0c57905ae4215 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 02:43:25 +0000 Subject: [PATCH 1124/2232] Make PtrLen usable for non-char data --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 10c6b40..a06ab62 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -327,7 +327,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { if needs_trycatch || needs_rust_error { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); - writeln!(out, " const char *ptr;"); + writeln!(out, " const void *ptr;"); writeln!(out, " size_t len;"); writeln!(out, "}};"); out.end_block("namespace repr"); @@ -339,7 +339,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "public:"); writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); writeln!(out, " Error error;"); - writeln!(out, " error.msg = repr.ptr;"); + writeln!(out, " error.msg = static_cast(repr.ptr);"); writeln!(out, " error.len = repr.len;"); writeln!(out, " return error;"); writeln!(out, " }}"); From 0356d33acbcee50f41aa8e44157d64b08522bcce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 03:33:39 +0000 Subject: [PATCH 1125/2232] Pass Str in PtrLen representation MSVC is hesitant about passing private fields in an extern "C" signature. Repro: struct Str1 { const char *ptr; size_t len; }; struct Str2 { private: const char *ptr; size_t len; }; extern "C" { Str1 str1(); Str2 str2(); } Warning from MSVC v19.27: warning C4190: 'str2' has C-linkage specified, but returns UDT 'Str2' which is incompatible with C --- diff --git a/gen/src/write.rs b/gen/src/write.rs index a06ab62..e3e9221 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -324,7 +324,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace"); - if needs_trycatch || needs_rust_error { + if needs_trycatch || needs_rust_error || needs_rust_str && !out.header { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); @@ -333,7 +333,28 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace repr"); } + if needs_rust_str && !out.header { + out.next_section(); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + writeln!( + out, + " static Str new_unchecked(repr::PtrLen repr) noexcept {{", + ); + writeln!(out, " Str str;"); + writeln!(out, " str.ptr = static_cast(repr.ptr);"); + writeln!(out, " str.len = repr.len;"); + writeln!(out, " return str;"); + writeln!(out, " }}"); + writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); + writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); + writeln!(out, " }}"); + writeln!(out, "}};"); + } + if needs_rust_error { + out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); @@ -619,6 +640,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), + Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::impl<::rust::Str>::repr("), Some(Type::SliceRefU8(_)) if !indirect_return => { write!(out, "::rust::Slice::Repr(") } @@ -638,6 +660,12 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } else if let Type::UniquePtr(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "({})", arg.ident); + } else if let Type::Str(_) = arg.ty { + write!( + out, + "::rust::impl<::rust::Str>::new_unchecked({})", + arg.ident, + ); } else if arg.ty == RustString { write!( out, @@ -658,7 +686,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { @@ -850,6 +878,7 @@ fn write_rust_function_shim_impl( write!(out, "("); } Type::Ref(_) => write!(out, "*"), + Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::new_unchecked("), _ => {} } } @@ -865,6 +894,7 @@ fn write_rust_function_shim_impl( write!(out, ", "); } match &arg.ty { + Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::repr("), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -873,7 +903,7 @@ fn write_rust_function_shim_impl( match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), - Type::SliceRefU8(_) => write!(out, ")"), + Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } @@ -893,7 +923,7 @@ fn write_rust_function_shim_impl( write!(out, ")"); if !indirect_return { if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) = ret { + if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) = ret { write!(out, ")"); } } @@ -964,6 +994,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { write_type(out, &ty.inner); write!(out, " *"); } + Some(Type::Str(_)) => write!(out, "::rust::repr::PtrLen "), Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), @@ -976,6 +1007,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { write_type_space(out, &ty.inner); write!(out, "*"); } + Type::Str(_) => write!(out, "::rust::repr::PtrLen "), Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } diff --git a/include/cxx.h b/include/cxx.h index de614d1..220f45f 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -79,6 +79,7 @@ public: ~Str() noexcept = default; private: + friend impl; // Not necessarily ABI compatible with &str. Codegen will translate to // cxx::rust_str::RustStr which matches this layout. const char *ptr; diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs index 3d3bf0f..1818af4 100644 --- a/tests/cxx_gen.rs +++ b/tests/cxx_gen.rs @@ -18,7 +18,7 @@ fn test_extern_c_function() { let output = str::from_utf8(&generated.implementation).unwrap(); // To avoid continual breakage we won't test every byte. // Let's look for the major features. - assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str foo)")); + assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)")); } #[test] @@ -28,5 +28,5 @@ fn test_impl_annotation() { let source = BRIDGE0.parse().unwrap(); let generated = generate_header_and_cc(source, &opt).unwrap(); let output = str::from_utf8(&generated.implementation).unwrap(); - assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str foo)")); + assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)")); } From a4eb943093a1e522650d4601dab79eeee063b947 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 03:36:38 +0000 Subject: [PATCH 1126/2232] Write impl more selectively --- diff --git a/gen/src/write.rs b/gen/src/write.rs index e3e9221..cadeb4e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -214,20 +214,21 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { let mut needs_manually_drop = false; let mut needs_maybe_uninit = false; let mut needs_trycatch = false; + let mut needs_rust_str_new_unchecked = false; + let mut needs_rust_str_repr = false; for api in apis { match api { Api::CxxFunction(efn) if !out.header => { if efn.throws { needs_trycatch = true; + } else if let Some(Type::Str(_)) = efn.ret { + needs_rust_str_repr = true; } for arg in &efn.args { - let bitcopy = match arg.ty { - Type::RustVec(_) => true, - _ => arg.ty == RustString, - }; - if bitcopy { - needs_unsafe_bitcopy = true; - break; + match arg.ty { + Type::Str(_) => needs_rust_str_new_unchecked = true, + Type::RustVec(_) => needs_unsafe_bitcopy = true, + _ => needs_unsafe_bitcopy |= arg.ty == RustString, } } } @@ -242,12 +243,16 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { for arg in &efn.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { needs_manually_drop = true; - break; + } + if let Type::Str(_) = arg.ty { + needs_rust_str_repr = true; } } if let Some(ret) = &efn.ret { if out.types.needs_indirect_abi(ret) { needs_maybe_uninit = true; + } else if let Type::Str(_) = ret { + needs_rust_str_new_unchecked = true; } } } @@ -324,7 +329,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace"); - if needs_trycatch || needs_rust_error || needs_rust_str && !out.header { + if needs_trycatch || needs_rust_error || needs_rust_str_new_unchecked || needs_rust_str_repr { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); @@ -333,23 +338,27 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace repr"); } - if needs_rust_str && !out.header { + if needs_rust_str_new_unchecked || needs_rust_str_repr { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - writeln!( - out, - " static Str new_unchecked(repr::PtrLen repr) noexcept {{", - ); - writeln!(out, " Str str;"); - writeln!(out, " str.ptr = static_cast(repr.ptr);"); - writeln!(out, " str.len = repr.len;"); - writeln!(out, " return str;"); - writeln!(out, " }}"); - writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); - writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); - writeln!(out, " }}"); + if needs_rust_str_new_unchecked { + writeln!( + out, + " static Str new_unchecked(repr::PtrLen repr) noexcept {{", + ); + writeln!(out, " Str str;"); + writeln!(out, " str.ptr = static_cast(repr.ptr);"); + writeln!(out, " str.len = repr.len;"); + writeln!(out, " return str;"); + writeln!(out, " }}"); + } + if needs_rust_str_repr { + writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); + writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); + writeln!(out, " }}"); + } writeln!(out, "}};"); } From 630af887178ee02c9f2671efcb085759920aa6c7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:03:47 +0000 Subject: [PATCH 1127/2232] Clean up unneeded explicit iter() --- diff --git a/gen/src/write.rs b/gen/src/write.rs index cadeb4e..e612bee 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -54,7 +54,7 @@ fn gen_namespace_contents( let apis = ns_entries.entries(); out.next_section(); - for api in apis.iter() { + for api in apis { match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), @@ -64,7 +64,7 @@ fn gen_namespace_contents( } let mut methods_for_type = HashMap::new(); - for api in apis.iter() { + for api in apis { if let Api::RustFunction(efn) = api { if let Some(receiver) = &efn.sig.receiver { methods_for_type From 9238b706a86a14753123d44f6736ac1f1d4ff7be Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Nov 01 2020 05:15:24 +0000 Subject: [PATCH 1128/2232] Test for namespace order sensitivity. --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fee5bcb..c79aff3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -101,6 +101,16 @@ pub mod ffi { z: usize, } + #[namespace = "first"] + struct First { + second: Box, + } + + #[namespace = "second"] + struct Second { + i: i32, + } + extern "C" { include!("tests/ffi/tests.h"); From f9213628a58e891f27032244ab0cdd0b3a3d269d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Nov 01 2020 05:25:42 +0000 Subject: [PATCH 1129/2232] Fix namespace forward declarations. This fixes the case where a lexicographically early namespace has a reference to a type in a lexicographically later namespace. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index f259222..dfdade2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -32,6 +32,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); + gen_namespace_forward_declarations(&apis_by_namespace, out); gen_namespace_contents(&apis_by_namespace, types, opt, header, out); if !header { @@ -44,13 +45,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - out_file } -fn gen_namespace_contents( - ns_entries: &NamespaceEntries, - types: &Types, - opt: &Opt, - header: bool, - out: &mut OutFile, -) { +fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut OutFile) { let apis = ns_entries.entries(); out.next_section(); @@ -63,6 +58,24 @@ fn gen_namespace_contents( } } + out.next_section(); + + for (child_ns, child_ns_entries) in ns_entries.children() { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_forward_declarations(&child_ns_entries, out); + writeln!(out, "}} // namespace {}", child_ns); + } +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = ns_entries.entries(); + let mut methods_for_type = HashMap::new(); for api in apis.iter() { if let Api::RustFunction(efn) = api { From 0aa9c773a1d298126ea0972fe33a2b52b92dfbc2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:39:47 +0000 Subject: [PATCH 1130/2232] Merge pull request #392 from adetaylor/namespace-forward-declarations Namespace forward declarations --- diff --git a/gen/src/write.rs b/gen/src/write.rs index e612bee..99d5e7d 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -32,6 +32,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); + gen_namespace_forward_declarations(&apis_by_namespace, out); gen_namespace_contents(&apis_by_namespace, types, opt, header, out); if !header { @@ -44,13 +45,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - out_file } -fn gen_namespace_contents( - ns_entries: &NamespaceEntries, - types: &Types, - opt: &Opt, - header: bool, - out: &mut OutFile, -) { +fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut OutFile) { let apis = ns_entries.entries(); out.next_section(); @@ -63,6 +58,24 @@ fn gen_namespace_contents( } } + out.next_section(); + + for (child_ns, child_ns_entries) in ns_entries.children() { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_forward_declarations(&child_ns_entries, out); + writeln!(out, "}} // namespace {}", child_ns); + } +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = ns_entries.entries(); + let mut methods_for_type = HashMap::new(); for api in apis { if let Api::RustFunction(efn) = api { diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fee5bcb..c79aff3 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -101,6 +101,16 @@ pub mod ffi { z: usize, } + #[namespace = "first"] + struct First { + second: Box, + } + + #[namespace = "second"] + struct Second { + i: i32, + } + extern "C" { include!("tests/ffi/tests.h"); From 4d14842342254c297968bfef3ae1ad4d6c3141fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:41:37 +0000 Subject: [PATCH 1131/2232] Remove unneeded Types parameter --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 99d5e7d..96e235f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -33,7 +33,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_forward_declarations(&apis_by_namespace, out); - gen_namespace_contents(&apis_by_namespace, types, opt, header, out); + gen_namespace_contents(&apis_by_namespace, opt, header, out); if !header { out.next_section(); @@ -69,7 +69,6 @@ fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut O fn gen_namespace_contents( ns_entries: &NamespaceEntries, - types: &Types, opt: &Opt, header: bool, out: &mut OutFile, @@ -92,13 +91,13 @@ fn gen_namespace_contents( match api { Api::Struct(strct) => { out.next_section(); - if !types.cxx.contains(&strct.ident.rust) { + if !out.types.cxx.contains(&strct.ident.rust) { write_struct(out, strct); } } Api::Enum(enm) => { out.next_section(); - if types.cxx.contains(&enm.ident.rust) { + if out.types.cxx.contains(&enm.ident.rust) { check_enum(out, enm); } else { write_enum(out, enm); @@ -117,7 +116,7 @@ fn gen_namespace_contents( out.next_section(); for api in apis { if let Api::TypeAlias(ety) = api { - if types.required_trivial.contains_key(&ety.ident.rust) { + if out.types.required_trivial.contains_key(&ety.ident.rust) { check_trivial_extern_type(out, &ety.ident.cxx) } } @@ -149,7 +148,7 @@ fn gen_namespace_contents( for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_contents(&child_ns_entries, types, opt, header, out); + gen_namespace_contents(&child_ns_entries, opt, header, out); writeln!(out, "}} // namespace {}", child_ns); } } From ce5a91f21467bd983e9b39f52237b526de57e88d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:42:08 +0000 Subject: [PATCH 1132/2232] Remove unneeded header parameter --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 96e235f..809a3af 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -33,7 +33,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_forward_declarations(&apis_by_namespace, out); - gen_namespace_contents(&apis_by_namespace, opt, header, out); + gen_namespace_contents(&apis_by_namespace, opt, out); if !header { out.next_section(); @@ -67,12 +67,7 @@ fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut O } } -fn gen_namespace_contents( - ns_entries: &NamespaceEntries, - opt: &Opt, - header: bool, - out: &mut OutFile, -) { +fn gen_namespace_contents(ns_entries: &NamespaceEntries, opt: &Opt, out: &mut OutFile) { let apis = ns_entries.entries(); let mut methods_for_type = HashMap::new(); @@ -122,7 +117,7 @@ fn gen_namespace_contents( } } - if !header { + if !out.header { out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { @@ -148,7 +143,7 @@ fn gen_namespace_contents( for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_contents(&child_ns_entries, opt, header, out); + gen_namespace_contents(&child_ns_entries, opt, out); writeln!(out, "}} // namespace {}", child_ns); } } From 04b8165b8c85c4815cadb7983daa13fa4ee4dcc0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:43:25 +0000 Subject: [PATCH 1133/2232] Keep OutFile consistently the first parameter --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 809a3af..4549c21 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -32,8 +32,8 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); - gen_namespace_forward_declarations(&apis_by_namespace, out); - gen_namespace_contents(&apis_by_namespace, opt, out); + gen_namespace_forward_declarations(out, &apis_by_namespace); + gen_namespace_contents(out, &apis_by_namespace, opt); if !header { out.next_section(); @@ -45,7 +45,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - out_file } -fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut OutFile) { +fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) { let apis = ns_entries.entries(); out.next_section(); @@ -62,12 +62,12 @@ fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut O for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_forward_declarations(&child_ns_entries, out); + gen_namespace_forward_declarations(out, &child_ns_entries); writeln!(out, "}} // namespace {}", child_ns); } } -fn gen_namespace_contents(ns_entries: &NamespaceEntries, opt: &Opt, out: &mut OutFile) { +fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: &Opt) { let apis = ns_entries.entries(); let mut methods_for_type = HashMap::new(); @@ -143,7 +143,7 @@ fn gen_namespace_contents(ns_entries: &NamespaceEntries, opt: &Opt, out: &mut Ou for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_contents(&child_ns_entries, opt, out); + gen_namespace_contents(out, &child_ns_entries, opt); writeln!(out, "}} // namespace {}", child_ns); } } From ef0473a6287842cb267d49a4cf5a8970c7749d36 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:44:55 +0000 Subject: [PATCH 1134/2232] Clean up redundant child_ns_entries borrows NamespaceEntries::children returns an iterator of (&Ident, &NamespaceEntries) so these child_ns_entries are already borrows. --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 4549c21..6ee6ad7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -62,7 +62,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_forward_declarations(out, &child_ns_entries); + gen_namespace_forward_declarations(out, child_ns_entries); writeln!(out, "}} // namespace {}", child_ns); } } @@ -143,7 +143,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); - gen_namespace_contents(out, &child_ns_entries, opt); + gen_namespace_contents(out, child_ns_entries, opt); writeln!(out, "}} // namespace {}", child_ns); } } From 3be0e1f53bd4fcc35caaed794c018204e83873e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:46:29 +0000 Subject: [PATCH 1135/2232] Move builtin tracking bools to module --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs new file mode 100644 index 0000000..a120eef --- /dev/null +++ b/gen/src/builtin.rs @@ -0,0 +1,24 @@ +#[derive(Default, PartialEq)] +pub struct Builtins { + pub panic: bool, + pub rust_string: bool, + pub rust_str: bool, + pub rust_slice: bool, + pub rust_box: bool, + pub rust_vec: bool, + pub rust_fn: bool, + pub rust_isize: bool, + pub unsafe_bitcopy: bool, + pub rust_error: bool, + pub manually_drop: bool, + pub maybe_uninit: bool, + pub trycatch: bool, + pub rust_str_new_unchecked: bool, + pub rust_str_repr: bool, +} + +impl Builtins { + pub fn new() -> Self { + Builtins::default() + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index e6e2c7c..e969dec 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,6 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. +mod builtin; mod check; pub(super) mod error; mod file; diff --git a/gen/src/out.rs b/gen/src/out.rs index 94a54e4..055f199 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,3 +1,4 @@ +use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::syntax::Types; use std::cell::RefCell; @@ -7,6 +8,7 @@ pub(crate) struct OutFile<'a> { pub header: bool, pub types: &'a Types<'a>, pub include: Includes, + pub builtin: Builtins, pub front: Content, content: RefCell, } @@ -23,6 +25,7 @@ impl<'a> OutFile<'a> { header, types, include: Includes::new(), + builtin: Builtins::new(), front: Content::new(), content: RefCell::new(Content::new()), } diff --git a/gen/src/write.rs b/gen/src/write.rs index 6ee6ad7..e638410 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -168,74 +168,59 @@ fn write_includes(out: &mut OutFile) { } fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { - let mut needs_panic = false; - let mut needs_rust_string = false; - let mut needs_rust_str = false; - let mut needs_rust_slice = false; - let mut needs_rust_box = false; - let mut needs_rust_vec = false; - let mut needs_rust_fn = false; - let mut needs_rust_isize = false; - let mut needs_unsafe_bitcopy = false; for ty in out.types { match ty { Type::RustBox(_) => { out.include.new = true; out.include.type_traits = true; - needs_rust_box = true; + out.builtin.rust_box = true; } Type::RustVec(_) => { out.include.array = true; out.include.new = true; out.include.type_traits = true; - needs_panic = true; - needs_rust_vec = true; - needs_unsafe_bitcopy = true; + out.builtin.panic = true; + out.builtin.rust_vec = true; + out.builtin.unsafe_bitcopy = true; } Type::Str(_) => { out.include.cstdint = true; out.include.string = true; - needs_rust_str = true; + out.builtin.rust_str = true; } Type::Fn(_) => { - needs_rust_fn = true; + out.builtin.rust_fn = true; } Type::Slice(_) | Type::SliceRefU8(_) => { - needs_rust_slice = true; + out.builtin.rust_slice = true; } ty if ty == Isize => { out.include.basetsd = true; - needs_rust_isize = true; + out.builtin.rust_isize = true; } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; out.include.string = true; - needs_rust_string = true; + out.builtin.rust_string = true; } _ => {} } } - let mut needs_rust_error = false; - let mut needs_manually_drop = false; - let mut needs_maybe_uninit = false; - let mut needs_trycatch = false; - let mut needs_rust_str_new_unchecked = false; - let mut needs_rust_str_repr = false; for api in apis { match api { Api::CxxFunction(efn) if !out.header => { if efn.throws { - needs_trycatch = true; + out.builtin.trycatch = true; } else if let Some(Type::Str(_)) = efn.ret { - needs_rust_str_repr = true; + out.builtin.rust_str_repr = true; } for arg in &efn.args { match arg.ty { - Type::Str(_) => needs_rust_str_new_unchecked = true, - Type::RustVec(_) => needs_unsafe_bitcopy = true, - _ => needs_unsafe_bitcopy |= arg.ty == RustString, + Type::Str(_) => out.builtin.rust_str_new_unchecked = true, + Type::RustVec(_) => out.builtin.unsafe_bitcopy = true, + _ => out.builtin.unsafe_bitcopy |= arg.ty == RustString, } } } @@ -243,23 +228,23 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { if efn.throws { out.include.exception = true; out.include.string = true; - needs_rust_str = true; - needs_rust_error = true; - needs_maybe_uninit = true; + out.builtin.rust_str = true; + out.builtin.rust_error = true; + out.builtin.maybe_uninit = true; } for arg in &efn.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { - needs_manually_drop = true; + out.builtin.manually_drop = true; } if let Type::Str(_) = arg.ty { - needs_rust_str_repr = true; + out.builtin.rust_str_repr = true; } } if let Some(ret) = &efn.ret { if out.types.needs_indirect_abi(ret) { - needs_maybe_uninit = true; + out.builtin.maybe_uninit = true; } else if let Type::Str(_) = ret { - needs_rust_str_new_unchecked = true; + out.builtin.rust_str_new_unchecked = true; } } } @@ -270,47 +255,35 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); - if needs_panic - || needs_rust_string - || needs_rust_str - || needs_rust_slice - || needs_rust_box - || needs_rust_vec - || needs_rust_fn - || needs_rust_error - || needs_rust_isize - || needs_unsafe_bitcopy - || needs_manually_drop - || needs_maybe_uninit - { + if out.builtin != Default::default() { writeln!(out, "// #include \"rust/cxx.h\""); } - include::write(out, needs_panic, "CXXBRIDGE05_PANIC"); + include::write(out, out.builtin.panic, "CXXBRIDGE05_PANIC"); - if needs_rust_string { + if out.builtin.rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } - if needs_rust_error { + if out.builtin.rust_error { out.begin_block("namespace"); writeln!(out, "template "); writeln!(out, "class impl;"); out.end_block("namespace"); } - include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING"); - include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR"); - include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE"); - include::write(out, needs_rust_box, "CXXBRIDGE05_RUST_BOX"); - include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - include::write(out, needs_rust_vec, "CXXBRIDGE05_RUST_VEC"); - include::write(out, needs_rust_fn, "CXXBRIDGE05_RUST_FN"); - include::write(out, needs_rust_error, "CXXBRIDGE05_RUST_ERROR"); - include::write(out, needs_rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + include::write(out, out.builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + include::write(out, out.builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + include::write(out, out.builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + include::write(out, out.builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + include::write(out, out.builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + include::write(out, out.builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + include::write(out, out.builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + include::write(out, out.builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + include::write(out, out.builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); - if needs_manually_drop { + if out.builtin.manually_drop { out.next_section(); out.include.utility = true; writeln!(out, "template "); @@ -324,7 +297,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } - if needs_maybe_uninit { + if out.builtin.maybe_uninit { out.next_section(); writeln!(out, "template "); writeln!(out, "union MaybeUninit {{"); @@ -336,7 +309,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace"); - if needs_trycatch || needs_rust_error || needs_rust_str_new_unchecked || needs_rust_str_repr { + if out.builtin.trycatch + || out.builtin.rust_error + || out.builtin.rust_str_new_unchecked + || out.builtin.rust_str_repr + { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); @@ -345,12 +322,12 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace repr"); } - if needs_rust_str_new_unchecked || needs_rust_str_repr { + if out.builtin.rust_str_new_unchecked || out.builtin.rust_str_repr { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - if needs_rust_str_new_unchecked { + if out.builtin.rust_str_new_unchecked { writeln!( out, " static Str new_unchecked(repr::PtrLen repr) noexcept {{", @@ -361,7 +338,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, " return str;"); writeln!(out, " }}"); } - if needs_rust_str_repr { + if out.builtin.rust_str_repr { writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); writeln!(out, " }}"); @@ -369,7 +346,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } - if needs_rust_error { + if out.builtin.rust_error { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); @@ -386,7 +363,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace"); out.end_block("namespace cxxbridge05"); - if needs_trycatch { + if out.builtin.trycatch { out.begin_block("namespace behavior"); out.include.exception = true; out.include.type_traits = true; From d5a5f447e110a52ea422430f302d8fa37be4a9e4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:55:53 +0000 Subject: [PATCH 1136/2232] Merge pull request #393 from dtolnay/builtin Move builtin tracking bools to module --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs new file mode 100644 index 0000000..a120eef --- /dev/null +++ b/gen/src/builtin.rs @@ -0,0 +1,24 @@ +#[derive(Default, PartialEq)] +pub struct Builtins { + pub panic: bool, + pub rust_string: bool, + pub rust_str: bool, + pub rust_slice: bool, + pub rust_box: bool, + pub rust_vec: bool, + pub rust_fn: bool, + pub rust_isize: bool, + pub unsafe_bitcopy: bool, + pub rust_error: bool, + pub manually_drop: bool, + pub maybe_uninit: bool, + pub trycatch: bool, + pub rust_str_new_unchecked: bool, + pub rust_str_repr: bool, +} + +impl Builtins { + pub fn new() -> Self { + Builtins::default() + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index e6e2c7c..e969dec 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,6 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. +mod builtin; mod check; pub(super) mod error; mod file; diff --git a/gen/src/out.rs b/gen/src/out.rs index 94a54e4..055f199 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,3 +1,4 @@ +use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::syntax::Types; use std::cell::RefCell; @@ -7,6 +8,7 @@ pub(crate) struct OutFile<'a> { pub header: bool, pub types: &'a Types<'a>, pub include: Includes, + pub builtin: Builtins, pub front: Content, content: RefCell, } @@ -23,6 +25,7 @@ impl<'a> OutFile<'a> { header, types, include: Includes::new(), + builtin: Builtins::new(), front: Content::new(), content: RefCell::new(Content::new()), } diff --git a/gen/src/write.rs b/gen/src/write.rs index 6ee6ad7..e638410 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -168,74 +168,59 @@ fn write_includes(out: &mut OutFile) { } fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { - let mut needs_panic = false; - let mut needs_rust_string = false; - let mut needs_rust_str = false; - let mut needs_rust_slice = false; - let mut needs_rust_box = false; - let mut needs_rust_vec = false; - let mut needs_rust_fn = false; - let mut needs_rust_isize = false; - let mut needs_unsafe_bitcopy = false; for ty in out.types { match ty { Type::RustBox(_) => { out.include.new = true; out.include.type_traits = true; - needs_rust_box = true; + out.builtin.rust_box = true; } Type::RustVec(_) => { out.include.array = true; out.include.new = true; out.include.type_traits = true; - needs_panic = true; - needs_rust_vec = true; - needs_unsafe_bitcopy = true; + out.builtin.panic = true; + out.builtin.rust_vec = true; + out.builtin.unsafe_bitcopy = true; } Type::Str(_) => { out.include.cstdint = true; out.include.string = true; - needs_rust_str = true; + out.builtin.rust_str = true; } Type::Fn(_) => { - needs_rust_fn = true; + out.builtin.rust_fn = true; } Type::Slice(_) | Type::SliceRefU8(_) => { - needs_rust_slice = true; + out.builtin.rust_slice = true; } ty if ty == Isize => { out.include.basetsd = true; - needs_rust_isize = true; + out.builtin.rust_isize = true; } ty if ty == RustString => { out.include.array = true; out.include.cstdint = true; out.include.string = true; - needs_rust_string = true; + out.builtin.rust_string = true; } _ => {} } } - let mut needs_rust_error = false; - let mut needs_manually_drop = false; - let mut needs_maybe_uninit = false; - let mut needs_trycatch = false; - let mut needs_rust_str_new_unchecked = false; - let mut needs_rust_str_repr = false; for api in apis { match api { Api::CxxFunction(efn) if !out.header => { if efn.throws { - needs_trycatch = true; + out.builtin.trycatch = true; } else if let Some(Type::Str(_)) = efn.ret { - needs_rust_str_repr = true; + out.builtin.rust_str_repr = true; } for arg in &efn.args { match arg.ty { - Type::Str(_) => needs_rust_str_new_unchecked = true, - Type::RustVec(_) => needs_unsafe_bitcopy = true, - _ => needs_unsafe_bitcopy |= arg.ty == RustString, + Type::Str(_) => out.builtin.rust_str_new_unchecked = true, + Type::RustVec(_) => out.builtin.unsafe_bitcopy = true, + _ => out.builtin.unsafe_bitcopy |= arg.ty == RustString, } } } @@ -243,23 +228,23 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { if efn.throws { out.include.exception = true; out.include.string = true; - needs_rust_str = true; - needs_rust_error = true; - needs_maybe_uninit = true; + out.builtin.rust_str = true; + out.builtin.rust_error = true; + out.builtin.maybe_uninit = true; } for arg in &efn.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { - needs_manually_drop = true; + out.builtin.manually_drop = true; } if let Type::Str(_) = arg.ty { - needs_rust_str_repr = true; + out.builtin.rust_str_repr = true; } } if let Some(ret) = &efn.ret { if out.types.needs_indirect_abi(ret) { - needs_maybe_uninit = true; + out.builtin.maybe_uninit = true; } else if let Type::Str(_) = ret { - needs_rust_str_new_unchecked = true; + out.builtin.rust_str_new_unchecked = true; } } } @@ -270,47 +255,35 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); - if needs_panic - || needs_rust_string - || needs_rust_str - || needs_rust_slice - || needs_rust_box - || needs_rust_vec - || needs_rust_fn - || needs_rust_error - || needs_rust_isize - || needs_unsafe_bitcopy - || needs_manually_drop - || needs_maybe_uninit - { + if out.builtin != Default::default() { writeln!(out, "// #include \"rust/cxx.h\""); } - include::write(out, needs_panic, "CXXBRIDGE05_PANIC"); + include::write(out, out.builtin.panic, "CXXBRIDGE05_PANIC"); - if needs_rust_string { + if out.builtin.rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } - if needs_rust_error { + if out.builtin.rust_error { out.begin_block("namespace"); writeln!(out, "template "); writeln!(out, "class impl;"); out.end_block("namespace"); } - include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING"); - include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR"); - include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE"); - include::write(out, needs_rust_box, "CXXBRIDGE05_RUST_BOX"); - include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - include::write(out, needs_rust_vec, "CXXBRIDGE05_RUST_VEC"); - include::write(out, needs_rust_fn, "CXXBRIDGE05_RUST_FN"); - include::write(out, needs_rust_error, "CXXBRIDGE05_RUST_ERROR"); - include::write(out, needs_rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + include::write(out, out.builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + include::write(out, out.builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + include::write(out, out.builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + include::write(out, out.builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + include::write(out, out.builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + include::write(out, out.builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + include::write(out, out.builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + include::write(out, out.builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + include::write(out, out.builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); - if needs_manually_drop { + if out.builtin.manually_drop { out.next_section(); out.include.utility = true; writeln!(out, "template "); @@ -324,7 +297,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } - if needs_maybe_uninit { + if out.builtin.maybe_uninit { out.next_section(); writeln!(out, "template "); writeln!(out, "union MaybeUninit {{"); @@ -336,7 +309,11 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.begin_block("namespace"); - if needs_trycatch || needs_rust_error || needs_rust_str_new_unchecked || needs_rust_str_repr { + if out.builtin.trycatch + || out.builtin.rust_error + || out.builtin.rust_str_new_unchecked + || out.builtin.rust_str_repr + { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); @@ -345,12 +322,12 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace repr"); } - if needs_rust_str_new_unchecked || needs_rust_str_repr { + if out.builtin.rust_str_new_unchecked || out.builtin.rust_str_repr { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - if needs_rust_str_new_unchecked { + if out.builtin.rust_str_new_unchecked { writeln!( out, " static Str new_unchecked(repr::PtrLen repr) noexcept {{", @@ -361,7 +338,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, " return str;"); writeln!(out, " }}"); } - if needs_rust_str_repr { + if out.builtin.rust_str_repr { writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); writeln!(out, " }}"); @@ -369,7 +346,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { writeln!(out, "}};"); } - if needs_rust_error { + if out.builtin.rust_error { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); @@ -386,7 +363,7 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { out.end_block("namespace"); out.end_block("namespace cxxbridge05"); - if needs_trycatch { + if out.builtin.trycatch { out.begin_block("namespace behavior"); out.include.exception = true; out.include.type_traits = true; From ec66d11dc4c65e3f1dc083c020f04a1b3f7fb9b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:01 +0000 Subject: [PATCH 1137/2232] Split detection and writing of builtins --- diff --git a/gen/src/write.rs b/gen/src/write.rs index e638410..a0bafc2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -27,6 +27,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_includes(out); write_include_cxxbridge(out, apis); + write_builtins(out); out.next_section(); @@ -251,7 +252,9 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { _ => {} } } +} +fn write_builtins(out: &mut OutFile) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); From 528200fa16e1a25e621f9fec81a95a48b1b4d2b0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:01 +0000 Subject: [PATCH 1138/2232] Merge two functions picking includes and builtins --- diff --git a/gen/src/write.rs b/gen/src/write.rs index a0bafc2..69f0144 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -25,8 +25,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - } } - write_includes(out); - write_include_cxxbridge(out, apis); + pick_includes_and_builtins(out, apis); write_builtins(out); out.next_section(); @@ -149,7 +148,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } -fn write_includes(out: &mut OutFile) { +fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { @@ -166,9 +165,7 @@ fn write_includes(out: &mut OutFile) { _ => {} } } -} -fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) { for ty in out.types { match ty { Type::RustBox(_) => { From b9da1468b61906d8182dad3a442646875a490468 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:01 +0000 Subject: [PATCH 1139/2232] Combine include-pick loop --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 69f0144..0514554 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -155,19 +155,19 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, + Some(Isize) => { + out.include.basetsd = true; + out.builtin.rust_isize = true; + } Some(CxxString) => out.include.string = true, - Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {} + Some(RustString) => { + out.include.array = true; + out.include.cstdint = true; + out.include.string = true; + out.builtin.rust_string = true; + } + Some(Bool) | Some(F32) | Some(F64) | None => {} }, - Type::RustBox(_) => out.include.type_traits = true, - Type::UniquePtr(_) => out.include.memory = true, - Type::CxxVector(_) => out.include.vector = true, - Type::SliceRefU8(_) => out.include.cstdint = true, - _ => {} - } - } - - for ty in out.types { - match ty { Type::RustBox(_) => { out.include.new = true; out.include.type_traits = true; @@ -181,26 +181,22 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { out.builtin.rust_vec = true; out.builtin.unsafe_bitcopy = true; } + Type::UniquePtr(_) => out.include.memory = true, Type::Str(_) => { out.include.cstdint = true; out.include.string = true; out.builtin.rust_str = true; } + Type::CxxVector(_) => out.include.vector = true, Type::Fn(_) => { out.builtin.rust_fn = true; } - Type::Slice(_) | Type::SliceRefU8(_) => { + Type::Slice(_) => { out.builtin.rust_slice = true; } - ty if ty == Isize => { - out.include.basetsd = true; - out.builtin.rust_isize = true; - } - ty if ty == RustString => { - out.include.array = true; + Type::SliceRefU8(_) => { out.include.cstdint = true; - out.include.string = true; - out.builtin.rust_string = true; + out.builtin.rust_slice = true; } _ => {} } From 12af7e48a7c5c367fb8e028ae672bc0fcde929a6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:01 +0000 Subject: [PATCH 1140/2232] Move writing includes to write.rs --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 309d8c3..b49cf82 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,6 +1,5 @@ use crate::gen::out::OutFile; use crate::syntax::{self, IncludeKind}; -use std::fmt::{self, Display}; /// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); @@ -64,7 +63,7 @@ pub struct Include { #[derive(Default, PartialEq)] pub struct Includes { - custom: Vec, + pub custom: Vec, pub array: bool, pub cstddef: bool, pub cstdint: bool, @@ -103,57 +102,3 @@ impl<'a> From<&'a syntax::Include> for Include { } } } - -impl Display for Includes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for include in &self.custom { - match include.kind { - IncludeKind::Quoted => { - writeln!(f, "#include \"{}\"", include.path.escape_default())?; - } - IncludeKind::Bracketed => { - writeln!(f, "#include <{}>", include.path)?; - } - } - } - if self.array { - writeln!(f, "#include ")?; - } - if self.cstddef { - writeln!(f, "#include ")?; - } - if self.cstdint { - writeln!(f, "#include ")?; - } - if self.cstring { - writeln!(f, "#include ")?; - } - if self.exception { - writeln!(f, "#include ")?; - } - if self.memory { - writeln!(f, "#include ")?; - } - if self.new { - writeln!(f, "#include ")?; - } - if self.string { - writeln!(f, "#include ")?; - } - if self.type_traits { - writeln!(f, "#include ")?; - } - if self.utility { - writeln!(f, "#include ")?; - } - if self.vector { - writeln!(f, "#include ")?; - } - if self.basetsd { - writeln!(f, "#if defined(_WIN32)")?; - writeln!(f, "#include ")?; - writeln!(f, "#endif")?; - } - Ok(()) - } -} diff --git a/gen/src/write.rs b/gen/src/write.rs index 0514554..9a1331e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,11 +1,12 @@ +use crate::gen::include::{self, Includes}; use crate::gen::namespace_organizer::NamespaceEntries; -use crate::gen::out::OutFile; -use crate::gen::{include, Opt}; +use crate::gen::out::{Content, OutFile}; +use crate::gen::Opt; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ - mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type, - Types, Var, + mangle, Api, CppName, Enum, ExternFn, ExternType, IncludeKind, ResolvableName, Signature, + Struct, Type, Types, Var, }; use proc_macro2::Ident; use std::collections::HashMap; @@ -40,7 +41,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_generic_instantiations(out); } - write!(out.front, "{}", out.include); + write_includes(&mut out.front, &out.include); out_file } @@ -247,6 +248,58 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } } +fn write_includes(out: &mut Content, include: &Includes) { + for include in &include.custom { + match include.kind { + IncludeKind::Quoted => { + writeln!(out, "#include \"{}\"", include.path.escape_default()); + } + IncludeKind::Bracketed => { + writeln!(out, "#include <{}>", include.path); + } + } + } + + if include.array { + writeln!(out, "#include "); + } + if include.cstddef { + writeln!(out, "#include "); + } + if include.cstdint { + writeln!(out, "#include "); + } + if include.cstring { + writeln!(out, "#include "); + } + if include.exception { + writeln!(out, "#include "); + } + if include.memory { + writeln!(out, "#include "); + } + if include.new { + writeln!(out, "#include "); + } + if include.string { + writeln!(out, "#include "); + } + if include.type_traits { + writeln!(out, "#include "); + } + if include.utility { + writeln!(out, "#include "); + } + if include.vector { + writeln!(out, "#include "); + } + if include.basetsd { + writeln!(out, "#if defined(_WIN32)"); + writeln!(out, "#include "); + writeln!(out, "#endif"); + } +} + fn write_builtins(out: &mut OutFile) { out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); From cb2189f3b196706264d1331cb6dbac8415091869 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1141/2232] Make section and block methods available on Content --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 055f199..9dd4a7d 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -33,23 +33,15 @@ impl<'a> OutFile<'a> { // Write a blank line if the preceding section had any contents. pub fn next_section(&mut self) { - let content = self.content.get_mut(); - content.section_pending = true; + self.content.get_mut().next_section(); } pub fn begin_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - content.blocks_pending.push(block); + self.content.get_mut().begin_block(block); } pub fn end_block(&mut self, block: &'static str) { - let content = self.content.get_mut(); - if content.blocks_pending.pop().is_none() { - content.bytes.push_str("} // "); - content.bytes.push_str(block); - content.bytes.push('\n'); - content.section_pending = true; - } + self.content.get_mut().end_block(block); } pub fn write_fmt(&self, args: Arguments) { @@ -82,10 +74,6 @@ impl Write for Content { } impl Content { - pub fn write_fmt(&mut self, args: Arguments) { - Write::write_fmt(self, args).unwrap(); - } - fn new() -> Self { Content { bytes: String::new(), @@ -94,6 +82,27 @@ impl Content { } } + pub fn next_section(&mut self) { + self.section_pending = true; + } + + pub fn begin_block(&mut self, block: &'static str) { + self.blocks_pending.push(block); + } + + pub fn end_block(&mut self, block: &'static str) { + if self.blocks_pending.pop().is_none() { + self.bytes.push_str("} // "); + self.bytes.push_str(block); + self.bytes.push('\n'); + self.section_pending = true; + } + } + + pub fn write_fmt(&mut self, args: Arguments) { + Write::write_fmt(self, args).unwrap(); + } + fn write(&mut self, b: &str) { if !b.is_empty() { if !self.blocks_pending.is_empty() { From 8810a54d80f0d62e46d842010e67e145f9470fe5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1142/2232] Move frontmatter content into Includes --- diff --git a/gen/src/include.rs b/gen/src/include.rs index b49cf82..ae0b202 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,5 +1,6 @@ -use crate::gen::out::OutFile; +use crate::gen::out::{Content, OutFile}; use crate::syntax::{self, IncludeKind}; +use std::ops::{Deref, DerefMut}; /// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); @@ -76,6 +77,7 @@ pub struct Includes { pub utility: bool, pub vector: bool, pub basetsd: bool, + pub content: Content, } impl Includes { @@ -102,3 +104,17 @@ impl<'a> From<&'a syntax::Include> for Include { } } } + +impl Deref for Includes { + type Target = Content; + + fn deref(&self) -> &Self::Target { + &self.content + } +} + +impl DerefMut for Includes { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.content + } +} diff --git a/gen/src/out.rs b/gen/src/out.rs index 9dd4a7d..6d475fc 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -9,10 +9,10 @@ pub(crate) struct OutFile<'a> { pub types: &'a Types<'a>, pub include: Includes, pub builtin: Builtins, - pub front: Content, content: RefCell, } +#[derive(Default)] pub struct Content { bytes: String, section_pending: bool, @@ -26,7 +26,6 @@ impl<'a> OutFile<'a> { types, include: Includes::new(), builtin: Builtins::new(), - front: Content::new(), content: RefCell::new(Content::new()), } } @@ -50,12 +49,12 @@ impl<'a> OutFile<'a> { } pub fn content(&self) -> Vec { - let front = &self.front.bytes; + let include = &self.include.content.bytes; let content = &self.content.borrow().bytes; - let len = front.len() + content.len() + 1; + let len = include.len() + content.len() + 1; let mut out = String::with_capacity(len); - out.push_str(front); - if !front.is_empty() && !content.is_empty() { + out.push_str(include); + if !include.is_empty() && !content.is_empty() { out.push('\n'); } out.push_str(content); @@ -73,13 +72,15 @@ impl Write for Content { } } +impl PartialEq for Content { + fn eq(&self, _other: &Content) -> bool { + true + } +} + impl Content { fn new() -> Self { - Content { - bytes: String::new(), - section_pending: false, - blocks_pending: Vec::new(), - } + Content::default() } pub fn next_section(&mut self) { diff --git a/gen/src/write.rs b/gen/src/write.rs index 9a1331e..f32cb5b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,7 +1,6 @@ -use crate::gen::include::{self, Includes}; use crate::gen::namespace_organizer::NamespaceEntries; -use crate::gen::out::{Content, OutFile}; -use crate::gen::Opt; +use crate::gen::out::OutFile; +use crate::gen::{include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -16,7 +15,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let out = &mut out_file; if header { - writeln!(out.front, "#pragma once"); + writeln!(out.include, "#pragma once"); } out.include.extend(&opt.include); @@ -41,7 +40,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_generic_instantiations(out); } - write_includes(&mut out.front, &out.include); + write_includes(out); out_file } @@ -248,7 +247,10 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } } -fn write_includes(out: &mut Content, include: &Includes) { +fn write_includes(out: &mut OutFile) { + let include = &mut out.include; + let out = &mut include.content; + for include in &include.custom { match include.kind { IncludeKind::Quoted => { From 8c14d9a638135113809f3179520ab569ef779781 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1143/2232] Add content block inside Builtins --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index a120eef..e28a62c 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -1,3 +1,5 @@ +use crate::gen::out::Content; + #[derive(Default, PartialEq)] pub struct Builtins { pub panic: bool, @@ -15,6 +17,7 @@ pub struct Builtins { pub trycatch: bool, pub rust_str_new_unchecked: bool, pub rust_str_repr: bool, + pub content: Content, } impl Builtins { diff --git a/gen/src/out.rs b/gen/src/out.rs index 6d475fc..99aecc2 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -50,11 +50,16 @@ impl<'a> OutFile<'a> { pub fn content(&self) -> Vec { let include = &self.include.content.bytes; + let builtin = &self.builtin.content.bytes; let content = &self.content.borrow().bytes; - let len = include.len() + content.len() + 1; + let len = include.len() + builtin.len() + content.len() + 2; let mut out = String::with_capacity(len); out.push_str(include); - if !include.is_empty() && !content.is_empty() { + if !out.is_empty() && !builtin.is_empty() { + out.push('\n'); + } + out.push_str(builtin); + if !out.is_empty() && !content.is_empty() { out.push('\n'); } out.push_str(content); From fd68e562fa83ab772046c3432747a0db4bc6bb05 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1144/2232] Write builtins directly to Builtins frontmatter --- diff --git a/gen/src/include.rs b/gen/src/include.rs index ae0b202..0ccb4d0 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,11 +1,11 @@ -use crate::gen::out::{Content, OutFile}; +use crate::gen::out::Content; use crate::syntax::{self, IncludeKind}; use std::ops::{Deref, DerefMut}; /// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); -pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) { +pub(super) fn write(out: &mut Content, needed: bool, guard: &str) { let ifndef = format!("#ifndef {}", guard); let define = format!("#define {}", guard); let endif = format!("#endif // {}", guard); diff --git a/gen/src/write.rs b/gen/src/write.rs index f32cb5b..1b30446 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -303,40 +303,45 @@ fn write_includes(out: &mut OutFile) { } fn write_builtins(out: &mut OutFile) { + if out.builtin == Default::default() { + return; + } + + let include = &mut out.include; + let builtin = &mut out.builtin; + let out = &mut builtin.content; + out.begin_block("namespace rust"); out.begin_block("inline namespace cxxbridge05"); + writeln!(out, "// #include \"rust/cxx.h\""); - if out.builtin != Default::default() { - writeln!(out, "// #include \"rust/cxx.h\""); - } - - include::write(out, out.builtin.panic, "CXXBRIDGE05_PANIC"); + include::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); - if out.builtin.rust_string { + if builtin.rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } - if out.builtin.rust_error { + if builtin.rust_error { out.begin_block("namespace"); writeln!(out, "template "); writeln!(out, "class impl;"); out.end_block("namespace"); } - include::write(out, out.builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); - include::write(out, out.builtin.rust_str, "CXXBRIDGE05_RUST_STR"); - include::write(out, out.builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); - include::write(out, out.builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); - include::write(out, out.builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - include::write(out, out.builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); - include::write(out, out.builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); - include::write(out, out.builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); - include::write(out, out.builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + include::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + include::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + include::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + include::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + include::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + include::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + include::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + include::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + include::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); - if out.builtin.manually_drop { + if builtin.manually_drop { out.next_section(); - out.include.utility = true; + include.utility = true; writeln!(out, "template "); writeln!(out, "union ManuallyDrop {{"); writeln!(out, " T value;"); @@ -348,7 +353,7 @@ fn write_builtins(out: &mut OutFile) { writeln!(out, "}};"); } - if out.builtin.maybe_uninit { + if builtin.maybe_uninit { out.next_section(); writeln!(out, "template "); writeln!(out, "union MaybeUninit {{"); @@ -360,10 +365,10 @@ fn write_builtins(out: &mut OutFile) { out.begin_block("namespace"); - if out.builtin.trycatch - || out.builtin.rust_error - || out.builtin.rust_str_new_unchecked - || out.builtin.rust_str_repr + if builtin.trycatch + || builtin.rust_error + || builtin.rust_str_new_unchecked + || builtin.rust_str_repr { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); @@ -373,12 +378,12 @@ fn write_builtins(out: &mut OutFile) { out.end_block("namespace repr"); } - if out.builtin.rust_str_new_unchecked || out.builtin.rust_str_repr { + if builtin.rust_str_new_unchecked || builtin.rust_str_repr { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - if out.builtin.rust_str_new_unchecked { + if builtin.rust_str_new_unchecked { writeln!( out, " static Str new_unchecked(repr::PtrLen repr) noexcept {{", @@ -389,7 +394,7 @@ fn write_builtins(out: &mut OutFile) { writeln!(out, " return str;"); writeln!(out, " }}"); } - if out.builtin.rust_str_repr { + if builtin.rust_str_repr { writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); writeln!(out, " }}"); @@ -397,7 +402,7 @@ fn write_builtins(out: &mut OutFile) { writeln!(out, "}};"); } - if out.builtin.rust_error { + if builtin.rust_error { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); @@ -414,11 +419,11 @@ fn write_builtins(out: &mut OutFile) { out.end_block("namespace"); out.end_block("namespace cxxbridge05"); - if out.builtin.trycatch { + if builtin.trycatch { out.begin_block("namespace behavior"); - out.include.exception = true; - out.include.type_traits = true; - out.include.utility = true; + include.exception = true; + include.type_traits = true; + include.utility = true; writeln!(out, "class missing {{}};"); writeln!(out, "missing trycatch(...);"); writeln!(out); From e629c6453a3171b60feeaf2e47e492abc810c981 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1145/2232] Defer writing builtins until after main content --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 1b30446..9696a49 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,6 +18,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - writeln!(out.include, "#pragma once"); } + pick_includes_and_builtins(out, apis); out.include.extend(&opt.include); for api in apis { if let Api::Include(include) = api { @@ -25,11 +26,6 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - } } - pick_includes_and_builtins(out, apis); - write_builtins(out); - - out.next_section(); - let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_forward_declarations(out, &apis_by_namespace); @@ -40,6 +36,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_generic_instantiations(out); } + write_builtins(out); write_includes(out); out_file From 880d1f86e56da711cbf288cee49f19c7b8dc1484 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1146/2232] Handle Api::Include as part of root namespace contents --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9696a49..095b2c2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -20,11 +20,6 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - pick_includes_and_builtins(out, apis); out.include.extend(&opt.include); - for api in apis { - if let Api::Include(include) = api { - out.include.insert(include); - } - } let apis_by_namespace = NamespaceEntries::new(apis); @@ -48,6 +43,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE out.next_section(); for api in apis { match api { + Api::Include(include) => out.include.insert(include), Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), From 3e278d72520767c73f705c48734cf4b768f86ea0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1147/2232] Move ifndef extractor to module --- diff --git a/gen/src/ifndef.rs b/gen/src/ifndef.rs new file mode 100644 index 0000000..b436266 --- /dev/null +++ b/gen/src/ifndef.rs @@ -0,0 +1,46 @@ +use crate::gen::include::HEADER; +use crate::gen::out::Content; + +pub(super) fn write(out: &mut Content, needed: bool, guard: &str) { + let ifndef = format!("#ifndef {}", guard); + let define = format!("#define {}", guard); + let endif = format!("#endif // {}", guard); + + let mut offset = 0; + loop { + let begin = find_line(offset, &ifndef); + let end = find_line(offset, &endif); + if let (Some(begin), Some(end)) = (begin, end) { + if !needed { + return; + } + out.next_section(); + if offset == 0 { + writeln!(out, "{}", ifndef); + writeln!(out, "{}", define); + } + for line in HEADER[begin + ifndef.len()..end].trim().lines() { + if line != define && !line.trim_start().starts_with("//") { + writeln!(out, "{}", line); + } + } + offset = end + endif.len(); + } else if offset == 0 { + panic!("not found in cxx.h header: {}", guard) + } else { + writeln!(out, "{}", endif); + return; + } + } +} + +fn find_line(mut offset: usize, line: &str) -> Option { + loop { + offset += HEADER[offset..].find(line)?; + let rest = &HEADER[offset + line.len()..]; + if rest.starts_with('\n') || rest.starts_with('\r') { + return Some(offset); + } + offset += line.len(); + } +} diff --git a/gen/src/include.rs b/gen/src/include.rs index 0ccb4d0..091f6c8 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -5,50 +5,6 @@ use std::ops::{Deref, DerefMut}; /// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); -pub(super) fn write(out: &mut Content, needed: bool, guard: &str) { - let ifndef = format!("#ifndef {}", guard); - let define = format!("#define {}", guard); - let endif = format!("#endif // {}", guard); - - let mut offset = 0; - loop { - let begin = find_line(offset, &ifndef); - let end = find_line(offset, &endif); - if let (Some(begin), Some(end)) = (begin, end) { - if !needed { - return; - } - out.next_section(); - if offset == 0 { - writeln!(out, "{}", ifndef); - writeln!(out, "{}", define); - } - for line in HEADER[begin + ifndef.len()..end].trim().lines() { - if line != define && !line.trim_start().starts_with("//") { - writeln!(out, "{}", line); - } - } - offset = end + endif.len(); - } else if offset == 0 { - panic!("not found in cxx.h header: {}", guard) - } else { - writeln!(out, "{}", endif); - return; - } - } -} - -fn find_line(mut offset: usize, line: &str) -> Option { - loop { - offset += HEADER[offset..].find(line)?; - let rest = &HEADER[offset + line.len()..]; - if rest.starts_with('\n') || rest.starts_with('\r') { - return Some(offset); - } - offset += line.len(); - } -} - /// A header to #include. /// /// The cxxbridge tool does not parse or even require the given paths to exist; diff --git a/gen/src/mod.rs b/gen/src/mod.rs index e969dec..d79a465 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -6,6 +6,7 @@ mod check; pub(super) mod error; mod file; pub(super) mod fs; +mod ifndef; pub(super) mod include; mod namespace_organizer; pub(super) mod out; diff --git a/gen/src/write.rs b/gen/src/write.rs index 095b2c2..2af11c6 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,6 +1,6 @@ use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; -use crate::gen::{include, Opt}; +use crate::gen::{ifndef, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -308,7 +308,7 @@ fn write_builtins(out: &mut OutFile) { out.begin_block("inline namespace cxxbridge05"); writeln!(out, "// #include \"rust/cxx.h\""); - include::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); + ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); if builtin.rust_string { out.next_section(); @@ -322,15 +322,15 @@ fn write_builtins(out: &mut OutFile) { out.end_block("namespace"); } - include::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); - include::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); - include::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); - include::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); - include::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - include::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); - include::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); - include::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); - include::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); if builtin.manually_drop { out.next_section(); From 2f3e90b10e88c02ed51f5e29f187664144f8ceb6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:02 +0000 Subject: [PATCH 1148/2232] Move include-related writes to include module --- diff --git a/gen/src/include.rs b/gen/src/include.rs index 091f6c8..8dc7146 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -1,4 +1,4 @@ -use crate::gen::out::Content; +use crate::gen::out::{Content, OutFile}; use crate::syntax::{self, IncludeKind}; use std::ops::{Deref, DerefMut}; @@ -46,6 +46,61 @@ impl Includes { } } +pub(super) fn write(out: &mut OutFile) { + let include = &mut out.include; + let out = &mut include.content; + + for include in &include.custom { + match include.kind { + IncludeKind::Quoted => { + writeln!(out, "#include \"{}\"", include.path.escape_default()); + } + IncludeKind::Bracketed => { + writeln!(out, "#include <{}>", include.path); + } + } + } + + if include.array { + writeln!(out, "#include "); + } + if include.cstddef { + writeln!(out, "#include "); + } + if include.cstdint { + writeln!(out, "#include "); + } + if include.cstring { + writeln!(out, "#include "); + } + if include.exception { + writeln!(out, "#include "); + } + if include.memory { + writeln!(out, "#include "); + } + if include.new { + writeln!(out, "#include "); + } + if include.string { + writeln!(out, "#include "); + } + if include.type_traits { + writeln!(out, "#include "); + } + if include.utility { + writeln!(out, "#include "); + } + if include.vector { + writeln!(out, "#include "); + } + if include.basetsd { + writeln!(out, "#if defined(_WIN32)"); + writeln!(out, "#include "); + writeln!(out, "#endif"); + } +} + impl<'a> Extend<&'a Include> for Includes { fn extend>(&mut self, iter: I) { self.custom.extend(iter.into_iter().cloned()); diff --git a/gen/src/write.rs b/gen/src/write.rs index 2af11c6..fc093e7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,11 +1,11 @@ use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; -use crate::gen::{ifndef, Opt}; +use crate::gen::{ifndef, include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ - mangle, Api, CppName, Enum, ExternFn, ExternType, IncludeKind, ResolvableName, Signature, - Struct, Type, Types, Var, + mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type, + Types, Var, }; use proc_macro2::Ident; use std::collections::HashMap; @@ -32,7 +32,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - } write_builtins(out); - write_includes(out); + include::write(out); out_file } @@ -240,61 +240,6 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } } -fn write_includes(out: &mut OutFile) { - let include = &mut out.include; - let out = &mut include.content; - - for include in &include.custom { - match include.kind { - IncludeKind::Quoted => { - writeln!(out, "#include \"{}\"", include.path.escape_default()); - } - IncludeKind::Bracketed => { - writeln!(out, "#include <{}>", include.path); - } - } - } - - if include.array { - writeln!(out, "#include "); - } - if include.cstddef { - writeln!(out, "#include "); - } - if include.cstdint { - writeln!(out, "#include "); - } - if include.cstring { - writeln!(out, "#include "); - } - if include.exception { - writeln!(out, "#include "); - } - if include.memory { - writeln!(out, "#include "); - } - if include.new { - writeln!(out, "#include "); - } - if include.string { - writeln!(out, "#include "); - } - if include.type_traits { - writeln!(out, "#include "); - } - if include.utility { - writeln!(out, "#include "); - } - if include.vector { - writeln!(out, "#include "); - } - if include.basetsd { - writeln!(out, "#if defined(_WIN32)"); - writeln!(out, "#include "); - writeln!(out, "#endif"); - } -} - fn write_builtins(out: &mut OutFile) { if out.builtin == Default::default() { return; From 3374d8d51bf11353a4359d70a931411274b09f92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 05:56:03 +0000 Subject: [PATCH 1149/2232] Move builtin-related writes to builtin module --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index e28a62c..a1393b5 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -1,4 +1,5 @@ -use crate::gen::out::Content; +use crate::gen::ifndef; +use crate::gen::out::{Content, OutFile}; #[derive(Default, PartialEq)] pub struct Builtins { @@ -25,3 +26,146 @@ impl Builtins { Builtins::default() } } + +pub(super) fn write(out: &mut OutFile) { + if out.builtin == Default::default() { + return; + } + + let include = &mut out.include; + let builtin = &mut out.builtin; + let out = &mut builtin.content; + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge05"); + writeln!(out, "// #include \"rust/cxx.h\""); + + ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); + + if builtin.rust_string { + out.next_section(); + writeln!(out, "struct unsafe_bitcopy_t;"); + } + + if builtin.rust_error { + out.begin_block("namespace"); + writeln!(out, "template "); + writeln!(out, "class impl;"); + out.end_block("namespace"); + } + + ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + + if builtin.manually_drop { + out.next_section(); + include.utility = true; + writeln!(out, "template "); + writeln!(out, "union ManuallyDrop {{"); + writeln!(out, " T value;"); + writeln!( + out, + " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", + ); + writeln!(out, " ~ManuallyDrop() {{}}"); + writeln!(out, "}};"); + } + + if builtin.maybe_uninit { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union MaybeUninit {{"); + writeln!(out, " T value;"); + writeln!(out, " MaybeUninit() {{}}"); + writeln!(out, " ~MaybeUninit() {{}}"); + writeln!(out, "}};"); + } + + out.begin_block("namespace"); + + if builtin.trycatch + || builtin.rust_error + || builtin.rust_str_new_unchecked + || builtin.rust_str_repr + { + out.begin_block("namespace repr"); + writeln!(out, "struct PtrLen final {{"); + writeln!(out, " const void *ptr;"); + writeln!(out, " size_t len;"); + writeln!(out, "}};"); + out.end_block("namespace repr"); + } + + if builtin.rust_str_new_unchecked || builtin.rust_str_repr { + out.next_section(); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + if builtin.rust_str_new_unchecked { + writeln!( + out, + " static Str new_unchecked(repr::PtrLen repr) noexcept {{", + ); + writeln!(out, " Str str;"); + writeln!(out, " str.ptr = static_cast(repr.ptr);"); + writeln!(out, " str.len = repr.len;"); + writeln!(out, " return str;"); + writeln!(out, " }}"); + } + if builtin.rust_str_repr { + writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); + writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); + writeln!(out, " }}"); + } + writeln!(out, "}};"); + } + + if builtin.rust_error { + out.next_section(); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); + writeln!(out, " Error error;"); + writeln!(out, " error.msg = static_cast(repr.ptr);"); + writeln!(out, " error.len = repr.len;"); + writeln!(out, " return error;"); + writeln!(out, " }}"); + writeln!(out, "}};"); + } + + out.end_block("namespace"); + out.end_block("namespace cxxbridge05"); + + if builtin.trycatch { + out.begin_block("namespace behavior"); + include.exception = true; + include.type_traits = true; + include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); + writeln!(out); + writeln!(out, "template "); + writeln!(out, "static typename ::std::enable_if<"); + writeln!( + out, + " ::std::is_same(), ::std::declval())),", + ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + out.end_block("namespace behavior"); + } + + out.end_block("namespace rust"); +} diff --git a/gen/src/write.rs b/gen/src/write.rs index fc093e7..5dcf91c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,6 +1,6 @@ use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; -use crate::gen::{ifndef, include, Opt}; +use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -31,7 +31,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_generic_instantiations(out); } - write_builtins(out); + builtin::write(out); include::write(out); out_file @@ -240,149 +240,6 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } } -fn write_builtins(out: &mut OutFile) { - if out.builtin == Default::default() { - return; - } - - let include = &mut out.include; - let builtin = &mut out.builtin; - let out = &mut builtin.content; - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); - writeln!(out, "// #include \"rust/cxx.h\""); - - ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); - - if builtin.rust_string { - out.next_section(); - writeln!(out, "struct unsafe_bitcopy_t;"); - } - - if builtin.rust_error { - out.begin_block("namespace"); - writeln!(out, "template "); - writeln!(out, "class impl;"); - out.end_block("namespace"); - } - - ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); - ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); - ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); - ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); - ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); - ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); - ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); - ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); - - if builtin.manually_drop { - out.next_section(); - include.utility = true; - writeln!(out, "template "); - writeln!(out, "union ManuallyDrop {{"); - writeln!(out, " T value;"); - writeln!( - out, - " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", - ); - writeln!(out, " ~ManuallyDrop() {{}}"); - writeln!(out, "}};"); - } - - if builtin.maybe_uninit { - out.next_section(); - writeln!(out, "template "); - writeln!(out, "union MaybeUninit {{"); - writeln!(out, " T value;"); - writeln!(out, " MaybeUninit() {{}}"); - writeln!(out, " ~MaybeUninit() {{}}"); - writeln!(out, "}};"); - } - - out.begin_block("namespace"); - - if builtin.trycatch - || builtin.rust_error - || builtin.rust_str_new_unchecked - || builtin.rust_str_repr - { - out.begin_block("namespace repr"); - writeln!(out, "struct PtrLen final {{"); - writeln!(out, " const void *ptr;"); - writeln!(out, " size_t len;"); - writeln!(out, "}};"); - out.end_block("namespace repr"); - } - - if builtin.rust_str_new_unchecked || builtin.rust_str_repr { - out.next_section(); - writeln!(out, "template <>"); - writeln!(out, "class impl final {{"); - writeln!(out, "public:"); - if builtin.rust_str_new_unchecked { - writeln!( - out, - " static Str new_unchecked(repr::PtrLen repr) noexcept {{", - ); - writeln!(out, " Str str;"); - writeln!(out, " str.ptr = static_cast(repr.ptr);"); - writeln!(out, " str.len = repr.len;"); - writeln!(out, " return str;"); - writeln!(out, " }}"); - } - if builtin.rust_str_repr { - writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); - writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); - writeln!(out, " }}"); - } - writeln!(out, "}};"); - } - - if builtin.rust_error { - out.next_section(); - writeln!(out, "template <>"); - writeln!(out, "class impl final {{"); - writeln!(out, "public:"); - writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); - writeln!(out, " Error error;"); - writeln!(out, " error.msg = static_cast(repr.ptr);"); - writeln!(out, " error.len = repr.len;"); - writeln!(out, " return error;"); - writeln!(out, " }}"); - writeln!(out, "}};"); - } - - out.end_block("namespace"); - out.end_block("namespace cxxbridge05"); - - if builtin.trycatch { - out.begin_block("namespace behavior"); - include.exception = true; - include.type_traits = true; - include.utility = true; - writeln!(out, "class missing {{}};"); - writeln!(out, "missing trycatch(...);"); - writeln!(out); - writeln!(out, "template "); - writeln!(out, "static typename ::std::enable_if<"); - writeln!( - out, - " ::std::is_same(), ::std::declval())),", - ); - writeln!(out, " missing>::value>::type"); - writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); - writeln!(out, " func();"); - writeln!(out, "}} catch (const ::std::exception &e) {{"); - writeln!(out, " fail(e.what());"); - writeln!(out, "}}"); - out.end_block("namespace behavior"); - } - - out.end_block("namespace rust"); -} - fn write_struct(out: &mut OutFile, strct: &Struct) { let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); From bc6ebde31ef0d214cfcf3464a85f4564916c0d8a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:05:32 +0000 Subject: [PATCH 1150/2232] Merge pull request #394 from dtolnay/builtin Move builtin-related writes to builtin module --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index e28a62c..a1393b5 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -1,4 +1,5 @@ -use crate::gen::out::Content; +use crate::gen::ifndef; +use crate::gen::out::{Content, OutFile}; #[derive(Default, PartialEq)] pub struct Builtins { @@ -25,3 +26,146 @@ impl Builtins { Builtins::default() } } + +pub(super) fn write(out: &mut OutFile) { + if out.builtin == Default::default() { + return; + } + + let include = &mut out.include; + let builtin = &mut out.builtin; + let out = &mut builtin.content; + + out.begin_block("namespace rust"); + out.begin_block("inline namespace cxxbridge05"); + writeln!(out, "// #include \"rust/cxx.h\""); + + ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); + + if builtin.rust_string { + out.next_section(); + writeln!(out, "struct unsafe_bitcopy_t;"); + } + + if builtin.rust_error { + out.begin_block("namespace"); + writeln!(out, "template "); + writeln!(out, "class impl;"); + out.end_block("namespace"); + } + + ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); + ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); + ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); + ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); + ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); + ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); + ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); + ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); + ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + + if builtin.manually_drop { + out.next_section(); + include.utility = true; + writeln!(out, "template "); + writeln!(out, "union ManuallyDrop {{"); + writeln!(out, " T value;"); + writeln!( + out, + " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", + ); + writeln!(out, " ~ManuallyDrop() {{}}"); + writeln!(out, "}};"); + } + + if builtin.maybe_uninit { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "union MaybeUninit {{"); + writeln!(out, " T value;"); + writeln!(out, " MaybeUninit() {{}}"); + writeln!(out, " ~MaybeUninit() {{}}"); + writeln!(out, "}};"); + } + + out.begin_block("namespace"); + + if builtin.trycatch + || builtin.rust_error + || builtin.rust_str_new_unchecked + || builtin.rust_str_repr + { + out.begin_block("namespace repr"); + writeln!(out, "struct PtrLen final {{"); + writeln!(out, " const void *ptr;"); + writeln!(out, " size_t len;"); + writeln!(out, "}};"); + out.end_block("namespace repr"); + } + + if builtin.rust_str_new_unchecked || builtin.rust_str_repr { + out.next_section(); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + if builtin.rust_str_new_unchecked { + writeln!( + out, + " static Str new_unchecked(repr::PtrLen repr) noexcept {{", + ); + writeln!(out, " Str str;"); + writeln!(out, " str.ptr = static_cast(repr.ptr);"); + writeln!(out, " str.len = repr.len;"); + writeln!(out, " return str;"); + writeln!(out, " }}"); + } + if builtin.rust_str_repr { + writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); + writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); + writeln!(out, " }}"); + } + writeln!(out, "}};"); + } + + if builtin.rust_error { + out.next_section(); + writeln!(out, "template <>"); + writeln!(out, "class impl final {{"); + writeln!(out, "public:"); + writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); + writeln!(out, " Error error;"); + writeln!(out, " error.msg = static_cast(repr.ptr);"); + writeln!(out, " error.len = repr.len;"); + writeln!(out, " return error;"); + writeln!(out, " }}"); + writeln!(out, "}};"); + } + + out.end_block("namespace"); + out.end_block("namespace cxxbridge05"); + + if builtin.trycatch { + out.begin_block("namespace behavior"); + include.exception = true; + include.type_traits = true; + include.utility = true; + writeln!(out, "class missing {{}};"); + writeln!(out, "missing trycatch(...);"); + writeln!(out); + writeln!(out, "template "); + writeln!(out, "static typename ::std::enable_if<"); + writeln!( + out, + " ::std::is_same(), ::std::declval())),", + ); + writeln!(out, " missing>::value>::type"); + writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); + writeln!(out, " func();"); + writeln!(out, "}} catch (const ::std::exception &e) {{"); + writeln!(out, " fail(e.what());"); + writeln!(out, "}}"); + out.end_block("namespace behavior"); + } + + out.end_block("namespace rust"); +} diff --git a/gen/src/write.rs b/gen/src/write.rs index fc093e7..5dcf91c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,6 +1,6 @@ use crate::gen::namespace_organizer::NamespaceEntries; use crate::gen::out::OutFile; -use crate::gen::{ifndef, include, Opt}; +use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -31,7 +31,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - write_generic_instantiations(out); } - write_builtins(out); + builtin::write(out); include::write(out); out_file @@ -240,149 +240,6 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } } -fn write_builtins(out: &mut OutFile) { - if out.builtin == Default::default() { - return; - } - - let include = &mut out.include; - let builtin = &mut out.builtin; - let out = &mut builtin.content; - - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); - writeln!(out, "// #include \"rust/cxx.h\""); - - ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); - - if builtin.rust_string { - out.next_section(); - writeln!(out, "struct unsafe_bitcopy_t;"); - } - - if builtin.rust_error { - out.begin_block("namespace"); - writeln!(out, "template "); - writeln!(out, "class impl;"); - out.end_block("namespace"); - } - - ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); - ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR"); - ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE"); - ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX"); - ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY"); - ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC"); - ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); - ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); - ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); - - if builtin.manually_drop { - out.next_section(); - include.utility = true; - writeln!(out, "template "); - writeln!(out, "union ManuallyDrop {{"); - writeln!(out, " T value;"); - writeln!( - out, - " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}", - ); - writeln!(out, " ~ManuallyDrop() {{}}"); - writeln!(out, "}};"); - } - - if builtin.maybe_uninit { - out.next_section(); - writeln!(out, "template "); - writeln!(out, "union MaybeUninit {{"); - writeln!(out, " T value;"); - writeln!(out, " MaybeUninit() {{}}"); - writeln!(out, " ~MaybeUninit() {{}}"); - writeln!(out, "}};"); - } - - out.begin_block("namespace"); - - if builtin.trycatch - || builtin.rust_error - || builtin.rust_str_new_unchecked - || builtin.rust_str_repr - { - out.begin_block("namespace repr"); - writeln!(out, "struct PtrLen final {{"); - writeln!(out, " const void *ptr;"); - writeln!(out, " size_t len;"); - writeln!(out, "}};"); - out.end_block("namespace repr"); - } - - if builtin.rust_str_new_unchecked || builtin.rust_str_repr { - out.next_section(); - writeln!(out, "template <>"); - writeln!(out, "class impl final {{"); - writeln!(out, "public:"); - if builtin.rust_str_new_unchecked { - writeln!( - out, - " static Str new_unchecked(repr::PtrLen repr) noexcept {{", - ); - writeln!(out, " Str str;"); - writeln!(out, " str.ptr = static_cast(repr.ptr);"); - writeln!(out, " str.len = repr.len;"); - writeln!(out, " return str;"); - writeln!(out, " }}"); - } - if builtin.rust_str_repr { - writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{"); - writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};"); - writeln!(out, " }}"); - } - writeln!(out, "}};"); - } - - if builtin.rust_error { - out.next_section(); - writeln!(out, "template <>"); - writeln!(out, "class impl final {{"); - writeln!(out, "public:"); - writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); - writeln!(out, " Error error;"); - writeln!(out, " error.msg = static_cast(repr.ptr);"); - writeln!(out, " error.len = repr.len;"); - writeln!(out, " return error;"); - writeln!(out, " }}"); - writeln!(out, "}};"); - } - - out.end_block("namespace"); - out.end_block("namespace cxxbridge05"); - - if builtin.trycatch { - out.begin_block("namespace behavior"); - include.exception = true; - include.type_traits = true; - include.utility = true; - writeln!(out, "class missing {{}};"); - writeln!(out, "missing trycatch(...);"); - writeln!(out); - writeln!(out, "template "); - writeln!(out, "static typename ::std::enable_if<"); - writeln!( - out, - " ::std::is_same(), ::std::declval())),", - ); - writeln!(out, " missing>::value>::type"); - writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{"); - writeln!(out, " func();"); - writeln!(out, "}} catch (const ::std::exception &e) {{"); - writeln!(out, " fail(e.what());"); - writeln!(out, "}}"); - out.end_block("namespace behavior"); - } - - out.end_block("namespace rust"); -} - fn write_struct(out: &mut OutFile, strct: &Struct) { let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); From 74d6d514391a52ffb7cb49e061f954fd0c01e2bb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:05:39 +0000 Subject: [PATCH 1151/2232] Select builtins lazily during code generation --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 5dcf91c..c690995 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,7 +18,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - writeln!(out.include, "#pragma once"); } - pick_includes_and_builtins(out, apis); + pick_includes_and_builtins(out); out.include.extend(&opt.include); let apis_by_namespace = NamespaceEntries::new(apis); @@ -141,7 +141,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } -fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { +fn pick_includes_and_builtins(out: &mut OutFile) { for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { @@ -194,50 +194,6 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { _ => {} } } - - for api in apis { - match api { - Api::CxxFunction(efn) if !out.header => { - if efn.throws { - out.builtin.trycatch = true; - } else if let Some(Type::Str(_)) = efn.ret { - out.builtin.rust_str_repr = true; - } - for arg in &efn.args { - match arg.ty { - Type::Str(_) => out.builtin.rust_str_new_unchecked = true, - Type::RustVec(_) => out.builtin.unsafe_bitcopy = true, - _ => out.builtin.unsafe_bitcopy |= arg.ty == RustString, - } - } - } - Api::RustFunction(efn) if !out.header => { - if efn.throws { - out.include.exception = true; - out.include.string = true; - out.builtin.rust_str = true; - out.builtin.rust_error = true; - out.builtin.maybe_uninit = true; - } - for arg in &efn.args { - if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { - out.builtin.manually_drop = true; - } - if let Type::Str(_) = arg.ty { - out.builtin.rust_str_repr = true; - } - } - if let Some(ret) = &efn.ret { - if out.types.needs_indirect_abi(ret) { - out.builtin.maybe_uninit = true; - } else if let Type::Str(_) = ret { - out.builtin.rust_str_new_unchecked = true; - } - } - } - _ => {} - } - } } fn write_struct(out: &mut OutFile, strct: &Struct) { @@ -469,6 +425,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: writeln!(out, ";"); write!(out, " "); if efn.throws { + out.builtin.trycatch = true; writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); @@ -484,7 +441,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::impl<::rust::Str>::repr("), + Some(Type::Str(_)) if !indirect_return => { + out.builtin.rust_str_repr = true; + write!(out, "::rust::impl<::rust::Str>::repr("); + } Some(Type::SliceRefU8(_)) if !indirect_return => { write!(out, "::rust::Slice::Repr(") } @@ -505,18 +465,21 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write_type(out, &arg.ty); write!(out, "({})", arg.ident); } else if let Type::Str(_) = arg.ty { + out.builtin.rust_str_new_unchecked = true; write!( out, "::rust::impl<::rust::Str>::new_unchecked({})", arg.ident, ); } else if arg.ty == RustString { + out.builtin.unsafe_bitcopy = true; write!( out, "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident, ); } else if let Type::RustVec(_) = arg.ty { + out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if out.types.needs_indirect_abi(&arg.ty) { @@ -698,6 +661,7 @@ fn write_rust_function_shim_impl( for arg in &sig.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; + out.builtin.manually_drop = true; write!(out, " ::rust::ManuallyDrop<"); write_type(out, &arg.ty); writeln!(out, "> {}$(::std::move({0}));", arg.ident); @@ -706,6 +670,7 @@ fn write_rust_function_shim_impl( write!(out, " "); let indirect_return = indirect_return(sig, out.types); if indirect_return { + out.builtin.maybe_uninit = true; write!(out, "::rust::MaybeUninit<"); write_type(out, sig.ret.as_ref().unwrap()); writeln!(out, "> return$;"); @@ -722,7 +687,10 @@ fn write_rust_function_shim_impl( write!(out, "("); } Type::Ref(_) => write!(out, "*"), - Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::new_unchecked("), + Type::Str(_) => { + out.builtin.rust_str_new_unchecked = true; + write!(out, "::rust::impl<::rust::Str>::new_unchecked("); + } _ => {} } } @@ -738,7 +706,10 @@ fn write_rust_function_shim_impl( write!(out, ", "); } match &arg.ty { - Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::repr("), + Type::Str(_) => { + out.builtin.rust_str_repr = true; + write!(out, "::rust::impl<::rust::Str>::repr("); + } Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -774,6 +745,8 @@ fn write_rust_function_shim_impl( } writeln!(out, ";"); if sig.throws { + out.include.exception = true; + out.builtin.rust_error = true; writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); From 6890ea4d9d5f221f6795678533a04f4a6e4e2735 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:12:14 +0000 Subject: [PATCH 1152/2232] Merge pull request #395 from dtolnay/builtin Select builtins lazily during code generation --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 5dcf91c..c690995 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,7 +18,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - writeln!(out.include, "#pragma once"); } - pick_includes_and_builtins(out, apis); + pick_includes_and_builtins(out); out.include.extend(&opt.include); let apis_by_namespace = NamespaceEntries::new(apis); @@ -141,7 +141,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } -fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { +fn pick_includes_and_builtins(out: &mut OutFile) { for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { @@ -194,50 +194,6 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { _ => {} } } - - for api in apis { - match api { - Api::CxxFunction(efn) if !out.header => { - if efn.throws { - out.builtin.trycatch = true; - } else if let Some(Type::Str(_)) = efn.ret { - out.builtin.rust_str_repr = true; - } - for arg in &efn.args { - match arg.ty { - Type::Str(_) => out.builtin.rust_str_new_unchecked = true, - Type::RustVec(_) => out.builtin.unsafe_bitcopy = true, - _ => out.builtin.unsafe_bitcopy |= arg.ty == RustString, - } - } - } - Api::RustFunction(efn) if !out.header => { - if efn.throws { - out.include.exception = true; - out.include.string = true; - out.builtin.rust_str = true; - out.builtin.rust_error = true; - out.builtin.maybe_uninit = true; - } - for arg in &efn.args { - if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { - out.builtin.manually_drop = true; - } - if let Type::Str(_) = arg.ty { - out.builtin.rust_str_repr = true; - } - } - if let Some(ret) = &efn.ret { - if out.types.needs_indirect_abi(ret) { - out.builtin.maybe_uninit = true; - } else if let Type::Str(_) = ret { - out.builtin.rust_str_new_unchecked = true; - } - } - } - _ => {} - } - } } fn write_struct(out: &mut OutFile, strct: &Struct) { @@ -469,6 +425,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: writeln!(out, ";"); write!(out, " "); if efn.throws { + out.builtin.trycatch = true; writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); @@ -484,7 +441,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), - Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::impl<::rust::Str>::repr("), + Some(Type::Str(_)) if !indirect_return => { + out.builtin.rust_str_repr = true; + write!(out, "::rust::impl<::rust::Str>::repr("); + } Some(Type::SliceRefU8(_)) if !indirect_return => { write!(out, "::rust::Slice::Repr(") } @@ -505,18 +465,21 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write_type(out, &arg.ty); write!(out, "({})", arg.ident); } else if let Type::Str(_) = arg.ty { + out.builtin.rust_str_new_unchecked = true; write!( out, "::rust::impl<::rust::Str>::new_unchecked({})", arg.ident, ); } else if arg.ty == RustString { + out.builtin.unsafe_bitcopy = true; write!( out, "::rust::String(::rust::unsafe_bitcopy, *{})", arg.ident, ); } else if let Type::RustVec(_) = arg.ty { + out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); } else if out.types.needs_indirect_abi(&arg.ty) { @@ -698,6 +661,7 @@ fn write_rust_function_shim_impl( for arg in &sig.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; + out.builtin.manually_drop = true; write!(out, " ::rust::ManuallyDrop<"); write_type(out, &arg.ty); writeln!(out, "> {}$(::std::move({0}));", arg.ident); @@ -706,6 +670,7 @@ fn write_rust_function_shim_impl( write!(out, " "); let indirect_return = indirect_return(sig, out.types); if indirect_return { + out.builtin.maybe_uninit = true; write!(out, "::rust::MaybeUninit<"); write_type(out, sig.ret.as_ref().unwrap()); writeln!(out, "> return$;"); @@ -722,7 +687,10 @@ fn write_rust_function_shim_impl( write!(out, "("); } Type::Ref(_) => write!(out, "*"), - Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::new_unchecked("), + Type::Str(_) => { + out.builtin.rust_str_new_unchecked = true; + write!(out, "::rust::impl<::rust::Str>::new_unchecked("); + } _ => {} } } @@ -738,7 +706,10 @@ fn write_rust_function_shim_impl( write!(out, ", "); } match &arg.ty { - Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::repr("), + Type::Str(_) => { + out.builtin.rust_str_repr = true; + write!(out, "::rust::impl<::rust::Str>::repr("); + } Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} @@ -774,6 +745,8 @@ fn write_rust_function_shim_impl( } writeln!(out, ";"); if sig.throws { + out.include.exception = true; + out.builtin.rust_error = true; writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); From 919085cd8b104460df58a761fed1c14ed8eedcb9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:12:25 +0000 Subject: [PATCH 1153/2232] Add builtin for PtrLen --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index a1393b5..8a65b5f 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -16,6 +16,7 @@ pub struct Builtins { pub manually_drop: bool, pub maybe_uninit: bool, pub trycatch: bool, + pub ptr_len: bool, pub rust_str_new_unchecked: bool, pub rust_str_repr: bool, pub content: Content, @@ -90,11 +91,7 @@ pub(super) fn write(out: &mut OutFile) { out.begin_block("namespace"); - if builtin.trycatch - || builtin.rust_error - || builtin.rust_str_new_unchecked - || builtin.rust_str_repr - { + if builtin.ptr_len { out.begin_block("namespace repr"); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); diff --git a/gen/src/write.rs b/gen/src/write.rs index c690995..8af2fe5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -353,6 +353,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write!(out, "{} ", annotation); } if efn.throws { + out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &efn.ret); @@ -425,6 +426,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: writeln!(out, ";"); write!(out, " "); if efn.throws { + out.builtin.ptr_len = true; out.builtin.trycatch = true; writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); @@ -551,6 +553,7 @@ fn write_rust_function_decl_impl( indirect_call: bool, ) { if sig.throws { + out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, &sig.ret); @@ -695,6 +698,7 @@ fn write_rust_function_shim_impl( } } if sig.throws { + out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen error$ = "); } write!(out, "{}(", invoke); @@ -811,7 +815,10 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => write!(out, "::rust::repr::PtrLen "), + Some(Type::Str(_)) => { + out.builtin.ptr_len = true; + write!(out, "::rust::repr::PtrLen "); + } Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), @@ -824,7 +831,10 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => write!(out, "::rust::repr::PtrLen "), + Type::Str(_) => { + out.builtin.ptr_len = true; + write!(out, "::rust::repr::PtrLen "); + } Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } From 36aa9e0bd861a83e9f9313524eaa8223626be3dc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:24:31 +0000 Subject: [PATCH 1154/2232] Eliminate Slice::Repr struct --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 8a65b5f..448b6f8 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -19,6 +19,8 @@ pub struct Builtins { pub ptr_len: bool, pub rust_str_new_unchecked: bool, pub rust_str_repr: bool, + pub rust_slice_new: bool, + pub rust_slice_repr: bool, pub content: Content, } @@ -124,6 +126,33 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } + if builtin.rust_slice_new || builtin.rust_slice_repr { + out.next_section(); + writeln!(out, "template "); + writeln!(out, "class impl> final {{"); + writeln!(out, "public:"); + if builtin.rust_slice_new { + writeln!( + out, + " static Slice slice(repr::PtrLen repr) noexcept {{", + ); + writeln!( + out, + " return {{static_cast(repr.ptr), repr.len}};", + ); + writeln!(out, " }}"); + } + if builtin.rust_slice_repr { + writeln!( + out, + " static repr::PtrLen repr(Slice slice) noexcept {{", + ); + writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};"); + writeln!(out, " }}"); + } + writeln!(out, "}};"); + } + if builtin.rust_error { out.next_section(); writeln!(out, "template <>"); diff --git a/gen/src/write.rs b/gen/src/write.rs index 8af2fe5..194241a 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -448,7 +448,8 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: write!(out, "::rust::impl<::rust::Str>::repr("); } Some(Type::SliceRefU8(_)) if !indirect_return => { - write!(out, "::rust::Slice::Repr(") + out.builtin.rust_slice_repr = true; + write!(out, "::rust::impl<::rust::Slice>::repr(") } _ => {} } @@ -484,6 +485,12 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident); + } else if let Type::SliceRefU8(_) = arg.ty { + write!( + out, + "::rust::Slice(static_cast({0}.ptr), {0}.len)", + arg.ident, + ); } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.ident); @@ -694,6 +701,10 @@ fn write_rust_function_shim_impl( out.builtin.rust_str_new_unchecked = true; write!(out, "::rust::impl<::rust::Str>::new_unchecked("); } + Type::SliceRefU8(_) => { + out.builtin.rust_slice_new = true; + write!(out, "::rust::impl<::rust::Slice>::slice("); + } _ => {} } } @@ -714,7 +725,10 @@ fn write_rust_function_shim_impl( out.builtin.rust_str_repr = true; write!(out, "::rust::impl<::rust::Str>::repr("); } - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr("), + Type::SliceRefU8(_) => { + out.builtin.rust_slice_repr = true; + write!(out, "::rust::impl<::rust::Slice>::repr("); + } ty if out.types.needs_indirect_abi(ty) => write!(out, "&"), _ => {} } @@ -742,7 +756,8 @@ fn write_rust_function_shim_impl( write!(out, ")"); if !indirect_return { if let Some(ret) = &sig.ret { - if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) = ret { + if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) = ret + { write!(out, ")"); } } @@ -788,7 +803,6 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { write_type(out, &ty.inner); write!(out, " *"); } - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr"), _ => write_type(out, ty), } } @@ -815,11 +829,10 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option) { write_type(out, &ty.inner); write!(out, " *"); } - Some(Type::Str(_)) => { + Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) => { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } - Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice::Repr "), Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "), _ => write_return_type(out, ty), } @@ -831,11 +844,10 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) { write_type_space(out, &ty.inner); write!(out, "*"); } - Type::Str(_) => { + Type::Str(_) | Type::SliceRefU8(_) => { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } - Type::SliceRefU8(_) => write!(out, "::rust::Slice::Repr "), _ => write_type_space(out, &arg.ty), } if out.types.needs_indirect_abi(&arg.ty) { diff --git a/include/cxx.h b/include/cxx.h index 220f45f..45c3828 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -104,20 +104,12 @@ public: Slice(const Slice &) noexcept = default; ~Slice() noexcept = default; - // Repr is PRIVATE; must not be used other than by our generated code. - // - // At present this class is only used for &[u8] slices. - // Not necessarily ABI compatible with &[u8]. Codegen will translate to - // cxx::rust_sliceu8::RustSliceU8 which matches this layout. - struct Repr final { - const T *ptr; - size_t len; - }; - Slice(Repr) noexcept; - explicit operator Repr() const noexcept; - private: - Repr repr; + friend impl; + // Not necessarily ABI compatible with &[T]. Codegen will translate to + // cxx::rust_sliceu8::RustSliceU8 which matches this layout. + const T *ptr; + size_t len; }; #endif // CXXBRIDGE05_RUST_SLICE @@ -332,32 +324,24 @@ inline size_t Str::length() const noexcept { return this->len; } #ifndef CXXBRIDGE05_RUST_SLICE #define CXXBRIDGE05_RUST_SLICE template -Slice::Slice() noexcept : repr(Repr{reinterpret_cast(this), 0}) {} +Slice::Slice() noexcept : ptr(reinterpret_cast(this)), len(0) {} template -Slice::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {} +Slice::Slice(const T *s, size_t count) noexcept : ptr(s), len(count) {} template const T *Slice::data() const noexcept { - return this->repr.ptr; + return this->ptr; } template size_t Slice::size() const noexcept { - return this->repr.len; + return this->len; } template size_t Slice::length() const noexcept { - return this->repr.len; -} - -template -Slice::Slice(Repr repr_) noexcept : repr(repr_) {} - -template -Slice::operator Repr() const noexcept { - return this->repr; + return this->len; } #endif // CXXBRIDGE05_RUST_SLICE From 98f2b5f6cd9b3842f0cd4e37831f9d70cc89cd42 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:38:01 +0000 Subject: [PATCH 1155/2232] Lockfile update --- diff --git a/third-party/BUCK b/third-party/BUCK index d07cda2..403b275 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -72,7 +72,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.44/src/**"]), + srcs = glob(["vendor/syn-1.0.48/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index a539bd2..bdded93 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -78,7 +78,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.44/src/**"]), + srcs = glob(["vendor/syn-1.0.48/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3a32f05..f9c5f60 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -174,9 +174,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "link-cplusplus" @@ -207,14 +207,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bdc5e856e51e685846fb6c13a1f5e5432946c2c90501bdc76a1319f19e29da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" [[package]] name = "ryu" @@ -267,9 +262,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.44" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ "proc-macro2", "quote", From ace45fa9423735accfa303919d49461e7c0a075a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:38:49 +0000 Subject: [PATCH 1156/2232] Re-enable c_take_callback test --- diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index c79aff3..742062d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -169,10 +169,7 @@ pub mod ffi { fn c_take_ref_rust_vec_string(v: &Vec); fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); - /* - // https://github.com/dtolnay/cxx/issues/232 fn c_take_callback(callback: fn(String) -> usize); - */ fn c_take_enum(e: Enum); fn c_take_ns_enum(e: AEnum); fn c_take_nested_ns_enum(e: ABEnum); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4abc456..a03ec2b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -385,12 +385,9 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { } } -/* -// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback) { callback("2020"); } -*/ void c_take_enum(Enum e) { if (e == Enum::AVal) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6b1ec1e..e551048 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -134,10 +134,7 @@ void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_string(const rust::Vec &v); void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); -/* -// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback); -*/ void c_take_enum(Enum e); void c_take_ns_enum(::A::AEnum e); void c_take_nested_ns_enum(::A::B::ABEnum e); diff --git a/tests/test.rs b/tests/test.rs index b651feb..a90b8fc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -149,8 +149,6 @@ fn test_c_take() { check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal)); } -/* -// https://github.com/dtolnay/cxx/issues/232 #[test] fn test_c_callback() { fn callback(s: String) -> usize { @@ -162,7 +160,6 @@ fn test_c_callback() { check!(ffi::c_take_callback(callback)); } -*/ #[test] fn test_c_call_r() { From b8ebeb0e8406e3a8cb20795e1e90fdc43e9ce492 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 06:54:26 +0000 Subject: [PATCH 1157/2232] Fix fn arg representation to eliminate warning on rust 1.46+ --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1bcaa1e..b25d830 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -594,7 +594,7 @@ fn expand_rust_function_shim_impl( quote!(#receiver_type::#ident) } }, - None => quote!(__extern), + None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)), }; call.extend(quote! { (#(#vars),*) }); @@ -662,7 +662,7 @@ fn expand_rust_function_shim_impl( }; let pointer = match invoke { - None => Some(quote!(__extern: #sig)), + None => Some(quote!(__extern: *const ())), Some(_) => None, }; From 7fa9d633365eca13acf47505f74849054ce32735 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:01:32 +0000 Subject: [PATCH 1158/2232] Merge pull request #396 from dtolnay/fn Fix fn arg representation to eliminate warning on rust 1.46+ --- diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 1bcaa1e..b25d830 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -594,7 +594,7 @@ fn expand_rust_function_shim_impl( quote!(#receiver_type::#ident) } }, - None => quote!(__extern), + None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)), }; call.extend(quote! { (#(#vars),*) }); @@ -662,7 +662,7 @@ fn expand_rust_function_shim_impl( }; let pointer = match invoke { - None => Some(quote!(__extern: #sig)), + None => Some(quote!(__extern: *const ())), Some(_) => None, }; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index c79aff3..742062d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -169,10 +169,7 @@ pub mod ffi { fn c_take_ref_rust_vec_string(v: &Vec); fn c_take_ref_rust_vec_index(v: &Vec); fn c_take_ref_rust_vec_copy(v: &Vec); - /* - // https://github.com/dtolnay/cxx/issues/232 fn c_take_callback(callback: fn(String) -> usize); - */ fn c_take_enum(e: Enum); fn c_take_ns_enum(e: AEnum); fn c_take_nested_ns_enum(e: ABEnum); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 4abc456..a03ec2b 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -385,12 +385,9 @@ void c_take_ref_rust_vec_copy(const rust::Vec &v) { } } -/* -// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback) { callback("2020"); } -*/ void c_take_enum(Enum e) { if (e == Enum::AVal) { diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 6b1ec1e..e551048 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -134,10 +134,7 @@ void c_take_ref_rust_vec(const rust::Vec &v); void c_take_ref_rust_vec_string(const rust::Vec &v); void c_take_ref_rust_vec_index(const rust::Vec &v); void c_take_ref_rust_vec_copy(const rust::Vec &v); -/* -// https://github.com/dtolnay/cxx/issues/232 void c_take_callback(rust::Fn callback); -*/ void c_take_enum(Enum e); void c_take_ns_enum(::A::AEnum e); void c_take_nested_ns_enum(::A::B::ABEnum e); diff --git a/tests/test.rs b/tests/test.rs index b651feb..a90b8fc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -149,8 +149,6 @@ fn test_c_take() { check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal)); } -/* -// https://github.com/dtolnay/cxx/issues/232 #[test] fn test_c_callback() { fn callback(s: String) -> usize { @@ -162,7 +160,6 @@ fn test_c_callback() { check!(ffi::c_take_callback(callback)); } -*/ #[test] fn test_c_call_r() { From 1192653a45bfd4e7fef7767a03b399070eaf5824 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:06:10 +0000 Subject: [PATCH 1159/2232] Move proc-macro2 fallback to before starting to parse --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index d79a465..ac8224e 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -102,12 +102,12 @@ fn generate_from_string(source: &str, opt: &Opt) -> Result { let shebang_end = source.find('\n').unwrap_or(source.len()); source = &source[shebang_end..]; } + proc_macro2::fallback::force(); let syntax: File = syn::parse_str(source)?; generate(syntax, opt) } pub(super) fn generate(syntax: File, opt: &Opt) -> Result { - proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let bridge = syntax .modules From 9ca2ff26d23e4aa4526880bc88448bf89f67caa8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:10:47 +0000 Subject: [PATCH 1160/2232] Wrap gen::generate comment to 80 columns --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index ac8224e..33ab9c9 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -122,9 +122,9 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { errors.propagate()?; check::typecheck(errors, apis, types); errors.propagate()?; - // Some callers may wish to generate both header and C++ - // from the same token stream to avoid parsing twice. But others - // only need to generate one or the other. + // Some callers may wish to generate both header and implementation from the + // same token stream to avoid parsing twice. Others only need to generate + // one or the other. Ok(GeneratedCode { header: if opt.gen_header { write::gen(apis, types, opt, true).content() From 42e0d6fecd08c33f2c3246261826067b238d99d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:11:07 +0000 Subject: [PATCH 1161/2232] Collect apis from multiple cxx::bridge in the same file --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 33ab9c9..9b732d2 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -108,20 +108,24 @@ fn generate_from_string(source: &str, opt: &Opt) -> Result { } pub(super) fn generate(syntax: File, opt: &Opt) -> Result { + if syntax.modules.is_empty() { + return Err(Error::NoBridgeMod); + } + + let ref mut apis = Vec::new(); let ref mut errors = Errors::new(); - let bridge = syntax - .modules - .into_iter() - .next() - .ok_or(Error::NoBridgeMod)?; - let ref namespace = bridge.namespace; - let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); + for bridge in syntax.modules { + let ref namespace = bridge.namespace; + let trusted = bridge.unsafety.is_some(); + apis.extend(syntax::parse_items(errors, bridge.content, trusted, namespace)); + } + let ref types = Types::collect(errors, apis); check::precheck(errors, apis, opt); errors.propagate()?; check::typecheck(errors, apis, types); errors.propagate()?; + // Some callers may wish to generate both header and implementation from the // same token stream to avoid parsing twice. Others only need to generate // one or the other. From 299731330ce9c69d64b42365817c3c662b3eb0fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:25:16 +0000 Subject: [PATCH 1162/2232] Merge pull request #397 from dtolnay/multiple Collect apis from multiple cxx::bridge in the same file --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 33ab9c9..9b732d2 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -108,20 +108,24 @@ fn generate_from_string(source: &str, opt: &Opt) -> Result { } pub(super) fn generate(syntax: File, opt: &Opt) -> Result { + if syntax.modules.is_empty() { + return Err(Error::NoBridgeMod); + } + + let ref mut apis = Vec::new(); let ref mut errors = Errors::new(); - let bridge = syntax - .modules - .into_iter() - .next() - .ok_or(Error::NoBridgeMod)?; - let ref namespace = bridge.namespace; - let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); + for bridge in syntax.modules { + let ref namespace = bridge.namespace; + let trusted = bridge.unsafety.is_some(); + apis.extend(syntax::parse_items(errors, bridge.content, trusted, namespace)); + } + let ref types = Types::collect(errors, apis); check::precheck(errors, apis, opt); errors.propagate()?; check::typecheck(errors, apis, types); errors.propagate()?; + // Some callers may wish to generate both header and implementation from the // same token stream to avoid parsing twice. Others only need to generate // one or the other. From f6fa7b1a2221301bfec450bd8bcf47e489b1f641 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 07:29:46 +0000 Subject: [PATCH 1163/2232] Skip ui tests on macOS builder too --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c9b65..f8f66dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,12 +35,13 @@ jobs: with: toolchain: ${{matrix.rust}} - name: Determine test suite subset - # Our Windows jobs are the longest running, so exclude the relatively - # slow compiletest from them to speed up end-to-end CI time, except - # during cron builds when no human is presumably waiting on the build. - # The extra coverage is not particularly valuable and we can still - # ensure the test is kept passing on the basis of the scheduled builds. - if: matrix.os == 'windows' && github.event_name != 'schedule' + # Our Windows and macOS jobs are the longest running, so exclude the + # relatively slow compiletest from them to speed up end-to-end CI time, + # except during cron builds when no human is presumably waiting on the + # build. The extra coverage is not particularly valuable and we can + # still ensure the test is kept passing on the basis of the scheduled + # builds. + if: matrix.os && github.event_name != 'schedule' run: echo '::set-env name=RUSTFLAGS::--cfg skip_ui_tests' - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace --exclude cxx-test-suite From 7907966838d67d1ada820ea4c056251abfc41185 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 17:12:05 +0000 Subject: [PATCH 1164/2232] Switch to namespace = "quoted::path" in docs and tests To preserve parity with item-level #[namespace = "..."] which is currently restricted by rustc to a quoted string. --- diff --git a/demo/src/main.rs b/demo/src/main.rs index ee7e093..8f62084 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -1,4 +1,4 @@ -#[cxx::bridge(namespace = org::example)] +#[cxx::bridge(namespace = "org::example")] mod ffi { struct SharedThing { z: i32, diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 4d9b986..3796431 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -32,7 +32,7 @@ use syn::parse_macro_input; /// attribute macro. /// /// ``` -/// #[cxx::bridge(namespace = mycompany::rust)] +/// #[cxx::bridge(namespace = "mycompany::rust")] /// # mod ffi {} /// ``` /// diff --git a/src/extern_type.rs b/src/extern_type.rs index b9c5386..f92ff40 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -28,7 +28,7 @@ use self::kind::{Kind, Opaque, Trivial}; /// ```no_run /// // file1.rs /// # mod file1 { -/// #[cxx::bridge(namespace = example)] +/// #[cxx::bridge(namespace = "example")] /// pub mod ffi { /// extern "C" { /// type Demo; @@ -39,7 +39,7 @@ use self::kind::{Kind, Opaque, Trivial}; /// # } /// /// // file2.rs -/// #[cxx::bridge(namespace = example)] +/// #[cxx::bridge(namespace = "example")] /// pub mod ffi { /// extern "C" { /// type Demo = crate::file1::ffi::Demo; @@ -78,7 +78,7 @@ use self::kind::{Kind, Opaque, Trivial}; /// type Kind = cxx::kind::Opaque; /// } /// -/// #[cxx::bridge(namespace = folly)] +/// #[cxx::bridge(namespace = "folly")] /// pub mod ffi { /// extern "C" { /// include!("rust_cxx_bindings.h"); diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index a11970d..cd76a7d 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -8,7 +8,7 @@ // Rustfmt mangles the extern type alias. // https://github.com/rust-lang/rustfmt/issues/4159 #[rustfmt::skip] -#[cxx::bridge(namespace = tests)] +#[cxx::bridge(namespace = "tests")] pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 742062d..f40ec6c 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -62,7 +62,7 @@ mod other { } } -#[cxx::bridge(namespace = tests)] +#[cxx::bridge(namespace = "tests")] pub mod ffi { #[derive(Clone)] struct Shared { diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 77bae06..899d45b 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -1,7 +1,7 @@ // Rustfmt mangles the extern type alias. // https://github.com/rust-lang/rustfmt/issues/4159 #[rustfmt::skip] -#[cxx::bridge(namespace = tests)] +#[cxx::bridge(namespace = "tests")] pub mod ffi { extern "C" { include!("tests/ffi/tests.h"); diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs index 81a9b3f..e3d1380 100644 --- a/tests/ui/wrong_type_id.rs +++ b/tests/ui/wrong_type_id.rs @@ -1,11 +1,11 @@ -#[cxx::bridge(namespace = folly)] +#[cxx::bridge(namespace = "folly")] mod here { extern "C" { type StringPiece; } } -#[cxx::bridge(namespace = folly)] +#[cxx::bridge(namespace = "folly")] mod there { extern "C" { type ByteRange = crate::here::StringPiece; From 159e712a542017e96dad3563b39717041c233425 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:35:44 +0000 Subject: [PATCH 1165/2232] Format with rustfmt 1.4.22-beta --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 9b732d2..453f47b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -117,7 +117,12 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { for bridge in syntax.modules { let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); - apis.extend(syntax::parse_items(errors, bridge.content, trusted, namespace)); + apis.extend(syntax::parse_items( + errors, + bridge.content, + trusted, + namespace, + )); } let ref types = Types::collect(errors, apis); From f9d34a11ed565d8818497879de42c02e0d11daeb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:35:53 +0000 Subject: [PATCH 1166/2232] Allow non-static string used as block header --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 99aecc2..337bc93 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -16,7 +16,7 @@ pub(crate) struct OutFile<'a> { pub struct Content { bytes: String, section_pending: bool, - blocks_pending: Vec<&'static str>, + blocks_pending: Vec, } impl<'a> OutFile<'a> { @@ -35,11 +35,11 @@ impl<'a> OutFile<'a> { self.content.get_mut().next_section(); } - pub fn begin_block(&mut self, block: &'static str) { + pub fn begin_block(&mut self, block: &str) { self.content.get_mut().begin_block(block); } - pub fn end_block(&mut self, block: &'static str) { + pub fn end_block(&mut self, block: &str) { self.content.get_mut().end_block(block); } @@ -92,11 +92,11 @@ impl Content { self.section_pending = true; } - pub fn begin_block(&mut self, block: &'static str) { - self.blocks_pending.push(block); + pub fn begin_block(&mut self, block: &str) { + self.blocks_pending.push(block.to_owned()); } - pub fn end_block(&mut self, block: &'static str) { + pub fn end_block(&mut self, block: &str) { if self.blocks_pending.pop().is_none() { self.bytes.push_str("} // "); self.bytes.push_str(block); @@ -116,7 +116,7 @@ impl Content { self.bytes.push('\n'); } for block in self.blocks_pending.drain(..) { - self.bytes.push_str(block); + self.bytes.push_str(&block); self.bytes.push_str(" {\n"); } self.section_pending = false; From 9de98d40eeb5313fc8e4bb0787e85f095410eaa4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:35:54 +0000 Subject: [PATCH 1167/2232] Clean up section spacing around namespace entries --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 194241a..9046d69 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -22,7 +22,6 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - out.include.extend(&opt.include); let apis_by_namespace = NamespaceEntries::new(apis); - gen_namespace_forward_declarations(out, &apis_by_namespace); gen_namespace_contents(out, &apis_by_namespace, opt); @@ -132,12 +131,11 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } - out.next_section(); - for (child_ns, child_ns_entries) in ns_entries.children() { - writeln!(out, "namespace {} {{", child_ns); + let block = format!("namespace {}", child_ns); + out.begin_block(&block); gen_namespace_contents(out, child_ns_entries, opt); - writeln!(out, "}} // namespace {}", child_ns); + out.end_block(&block); } } From fe40ff2b2811efe4ed0fc8dad9279f087626268c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:35:54 +0000 Subject: [PATCH 1168/2232] Tighten spacing in between forward declarations --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9046d69..7c6d098 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -39,7 +39,6 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) { let apis = ns_entries.entries(); - out.next_section(); for api in apis { match api { Api::Include(include) => out.include.insert(include), @@ -50,8 +49,6 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } - out.next_section(); - for (child_ns, child_ns_entries) in ns_entries.children() { writeln!(out, "namespace {} {{", child_ns); gen_namespace_forward_declarations(out, child_ns_entries); From 60e7aa67e28f3cd81a63fa6b9d5198d4f232e738 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:37:28 +0000 Subject: [PATCH 1169/2232] Move non-boilerplate impls out of impls.rs --- diff --git a/syntax/impls.rs b/syntax/impls.rs index f4c5b05..a4b393a 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -1,13 +1,8 @@ -use crate::syntax::{ - Api, CppName, ExternFn, Impl, Include, Namespace, Pair, Receiver, Ref, ResolvableName, - Signature, Slice, Symbol, Ty1, Type, Types, -}; -use proc_macro2::{Ident, Span}; +use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::{Deref, DerefMut}; -use syn::Token; impl PartialEq for Include { fn eq(&self, other: &Include) -> bool { @@ -297,86 +292,3 @@ impl Borrow for &Impl { &self.ty } } - -impl Pair { - /// Use this constructor when the item can't have a different - /// name in Rust and C++. - pub fn new(ns: Namespace, ident: Ident) -> Self { - Self { - rust: ident.clone(), - cxx: CppName::new(ns, ident), - } - } - - /// Use this constructor when attributes such as #[rust_name] - /// can be used to potentially give a different name in Rust vs C++. - pub fn new_from_differing_names(ns: Namespace, cxx_ident: Ident, rust_ident: Ident) -> Self { - Self { - rust: rust_ident, - cxx: CppName::new(ns, cxx_ident), - } - } -} - -impl ResolvableName { - pub fn new(ident: Ident) -> Self { - Self { rust: ident } - } - - pub fn make_self(span: Span) -> Self { - Self { - rust: Token![Self](span).into(), - } - } - - pub fn is_self(&self) -> bool { - self.rust == "Self" - } - - pub fn span(&self) -> Span { - self.rust.span() - } - - pub fn to_symbol(&self, types: &Types) -> Symbol { - types.resolve(self).to_symbol() - } -} - -impl Api { - pub fn get_namespace(&self) -> Option<&Namespace> { - match self { - Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), - Api::CxxType(cty) => Some(&cty.ident.cxx.ns), - Api::Enum(enm) => Some(&enm.ident.cxx.ns), - Api::Struct(strct) => Some(&strct.ident.cxx.ns), - Api::RustType(rty) => Some(&rty.ident.cxx.ns), - Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), - Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, - } - } -} - -impl CppName { - pub fn new(ns: Namespace, ident: Ident) -> Self { - Self { ns, ident } - } - - fn iter_all_segments(&self) -> impl Iterator { - self.ns.iter().chain(std::iter::once(&self.ident)) - } - - fn join(&self, sep: &str) -> String { - self.iter_all_segments() - .map(|s| s.to_string()) - .collect::>() - .join(sep) - } - - pub fn to_symbol(&self) -> Symbol { - Symbol::from_idents(self.iter_all_segments()) - } - - pub fn to_fully_qualified(&self) -> String { - format!("::{}", self.join("::")) - } -} diff --git a/syntax/mod.rs b/syntax/mod.rs index 8d3b8a2..90af17f 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -11,6 +11,7 @@ pub mod file; pub mod ident; mod impls; pub mod mangle; +mod names; pub mod namespace; mod parse; pub mod qualified; diff --git a/syntax/names.rs b/syntax/names.rs new file mode 100644 index 0000000..a61ffc3 --- /dev/null +++ b/syntax/names.rs @@ -0,0 +1,72 @@ +use crate::syntax::{CppName, Namespace, Pair, ResolvableName, Symbol, Types}; +use proc_macro2::{Ident, Span}; +use syn::Token; + +impl Pair { + /// Use this constructor when the item can't have a different + /// name in Rust and C++. + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { + rust: ident.clone(), + cxx: CppName::new(ns, ident), + } + } + + /// Use this constructor when attributes such as #[rust_name] + /// can be used to potentially give a different name in Rust vs C++. + pub fn new_from_differing_names(ns: Namespace, cxx_ident: Ident, rust_ident: Ident) -> Self { + Self { + rust: rust_ident, + cxx: CppName::new(ns, cxx_ident), + } + } +} + +impl ResolvableName { + pub fn new(ident: Ident) -> Self { + Self { rust: ident } + } + + pub fn make_self(span: Span) -> Self { + Self { + rust: Token![Self](span).into(), + } + } + + pub fn is_self(&self) -> bool { + self.rust == "Self" + } + + pub fn span(&self) -> Span { + self.rust.span() + } + + pub fn to_symbol(&self, types: &Types) -> Symbol { + types.resolve(self).to_symbol() + } +} + +impl CppName { + pub fn new(ns: Namespace, ident: Ident) -> Self { + Self { ns, ident } + } + + fn iter_all_segments(&self) -> impl Iterator { + self.ns.iter().chain(std::iter::once(&self.ident)) + } + + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } + + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) + } +} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 5eb203a..41a38dd 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,4 +1,5 @@ use crate::syntax::qualified::QualifiedName; +use crate::syntax::Api; #[cfg(test)] use proc_macro2::Span; use quote::IdentFragment; @@ -79,3 +80,17 @@ impl<'a> IntoIterator for &'a Namespace { self.iter() } } + +impl Api { + pub fn get_namespace(&self) -> Option<&Namespace> { + match self { + Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), + Api::CxxType(cty) => Some(&cty.ident.cxx.ns), + Api::Enum(enm) => Some(&enm.ident.cxx.ns), + Api::Struct(strct) => Some(&strct.ident.cxx.ns), + Api::RustType(rty) => Some(&rty.ident.cxx.ns), + Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), + Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, + } + } +} From 6ec411103838868dc72f836f757206e8ffbaf4d3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:40:38 +0000 Subject: [PATCH 1170/2232] Condense get_namespace's match --- diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 41a38dd..5f368e8 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -84,12 +84,10 @@ impl<'a> IntoIterator for &'a Namespace { impl Api { pub fn get_namespace(&self) -> Option<&Namespace> { match self { - Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), - Api::CxxType(cty) => Some(&cty.ident.cxx.ns), + Api::CxxFunction(efn) | Api::RustFunction(efn) => Some(&efn.ident.cxx.ns), + Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident.cxx.ns), Api::Enum(enm) => Some(&enm.ident.cxx.ns), Api::Struct(strct) => Some(&strct.ident.cxx.ns), - Api::RustType(rty) => Some(&rty.ident.cxx.ns), - Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, } } From 68a12180a86f66e4c72138ad70e4e3dc9833ec8e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:42:03 +0000 Subject: [PATCH 1171/2232] Clean up get_ prefix from accessor method name --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index 525c367..59a58ad 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -29,7 +29,7 @@ impl<'a> NamespaceEntries<'a> { let mut kids_by_child_ns = BTreeMap::new(); for api in apis { - if let Some(ns) = api.get_namespace() { + if let Some(ns) = api.namespace() { let first_ns_elem = ns.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 5f368e8..4a37f7a 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -82,7 +82,7 @@ impl<'a> IntoIterator for &'a Namespace { } impl Api { - pub fn get_namespace(&self) -> Option<&Namespace> { + pub fn namespace(&self) -> Option<&Namespace> { match self { Api::CxxFunction(efn) | Api::RustFunction(efn) => Some(&efn.ident.cxx.ns), Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident.cxx.ns), From f8df1bed74a624d66613017bdd6a94c3fd7b693a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:44:36 +0000 Subject: [PATCH 1172/2232] Condense an iter + collect into from_iter --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index 59a58ad..aaf0722 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -1,6 +1,7 @@ use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; +use std::iter::FromIterator; pub struct NamespaceEntries<'a> { entries: Vec<&'a Api>, @@ -9,7 +10,7 @@ pub struct NamespaceEntries<'a> { impl<'a> NamespaceEntries<'a> { pub fn new(apis: &'a [Api]) -> Self { - let api_refs = apis.iter().collect::>(); + let api_refs = Vec::from_iter(apis); Self::sort_by_inner_namespace(api_refs, 0) } From 9e4e7504a05f745ad918a7adf16fb2f13d3e1a64 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:47:42 +0000 Subject: [PATCH 1173/2232] Clarify the distinction of NamespaceEntries contents --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index aaf0722..b4c8a93 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -4,8 +4,8 @@ use std::collections::BTreeMap; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { - entries: Vec<&'a Api>, - children: BTreeMap<&'a Ident, NamespaceEntries<'a>>, + direct: Vec<&'a Api>, + nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } impl<'a> NamespaceEntries<'a> { @@ -14,18 +14,18 @@ impl<'a> NamespaceEntries<'a> { Self::sort_by_inner_namespace(api_refs, 0) } - pub fn entries(&self) -> &[&'a Api] { - &self.entries + pub fn direct_content(&self) -> &[&'a Api] { + &self.direct } - pub fn children(&self) -> impl Iterator { - self.children.iter().map(|(k, entries)| (*k, entries)) + pub fn nested_content(&self) -> impl Iterator { + self.nested.iter().map(|(k, entries)| (*k, entries)) } fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { let mut root = NamespaceEntries { - entries: Vec::new(), - children: BTreeMap::new(), + direct: Vec::new(), + nested: BTreeMap::new(), }; let mut kids_by_child_ns = BTreeMap::new(); @@ -38,11 +38,11 @@ impl<'a> NamespaceEntries<'a> { continue; } } - root.entries.push(api); + root.direct.push(api); } for (k, v) in kids_by_child_ns.into_iter() { - root.children + root.nested .insert(k, Self::sort_by_inner_namespace(v, depth + 1)); } @@ -73,32 +73,32 @@ mod tests { make_api(Some("D"), "J"), ]; let ns = NamespaceEntries::new(&entries); - let root_entries = ns.entries(); + let root_entries = ns.direct_content(); assert_eq!(root_entries.len(), 3); assert_ident(root_entries[0], "C"); assert_ident(root_entries[1], "A"); assert_ident(root_entries[2], "B"); - let mut kids = ns.children(); + let mut kids = ns.nested_content(); let (d_id, d_nse) = kids.next().unwrap(); assert_eq!(d_id.to_string(), "D"); let (g_id, g_nse) = kids.next().unwrap(); assert_eq!(g_id.to_string(), "G"); assert!(kids.next().is_none()); - let d_nse_entries = d_nse.entries(); + let d_nse_entries = d_nse.direct_content(); assert_eq!(d_nse_entries.len(), 3); assert_ident(d_nse_entries[0], "F"); assert_ident(d_nse_entries[1], "I"); assert_ident(d_nse_entries[2], "J"); - let g_nse_entries = g_nse.entries(); + let g_nse_entries = g_nse.direct_content(); assert_eq!(g_nse_entries.len(), 2); assert_ident(g_nse_entries[0], "E"); assert_ident(g_nse_entries[1], "H"); - let mut g_kids = g_nse.children(); + let mut g_kids = g_nse.nested_content(); assert!(g_kids.next().is_none()); - let mut d_kids = d_nse.children(); + let mut d_kids = d_nse.nested_content(); let (k_id, k_nse) = d_kids.next().unwrap(); assert_eq!(k_id.to_string(), "K"); - let k_nse_entries = k_nse.entries(); + let k_nse_entries = k_nse.direct_content(); assert_eq!(k_nse_entries.len(), 2); assert_ident(k_nse_entries[0], "L"); assert_ident(k_nse_entries[1], "M"); diff --git a/gen/src/write.rs b/gen/src/write.rs index 7c6d098..5ca3dc5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -37,7 +37,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - } fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) { - let apis = ns_entries.entries(); + let apis = ns_entries.direct_content(); for api in apis { match api { @@ -49,7 +49,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } - for (child_ns, child_ns_entries) in ns_entries.children() { + for (child_ns, child_ns_entries) in ns_entries.nested_content() { writeln!(out, "namespace {} {{", child_ns); gen_namespace_forward_declarations(out, child_ns_entries); writeln!(out, "}} // namespace {}", child_ns); @@ -57,7 +57,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: &Opt) { - let apis = ns_entries.entries(); + let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); for api in apis { @@ -128,7 +128,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } - for (child_ns, child_ns_entries) in ns_entries.children() { + for (child_ns, child_ns_entries) in ns_entries.nested_content() { let block = format!("namespace {}", child_ns); out.begin_block(&block); gen_namespace_contents(out, child_ns_entries, opt); From 7943e504d9d0e9af66200903232f0f4fda827cab Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 20:51:31 +0000 Subject: [PATCH 1174/2232] Remove 'child' naming --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 5ca3dc5..9f57f7d 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -49,10 +49,10 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } - for (child_ns, child_ns_entries) in ns_entries.nested_content() { - writeln!(out, "namespace {} {{", child_ns); - gen_namespace_forward_declarations(out, child_ns_entries); - writeln!(out, "}} // namespace {}", child_ns); + for (namespace, nested_ns_entries) in ns_entries.nested_content() { + writeln!(out, "namespace {} {{", namespace); + gen_namespace_forward_declarations(out, nested_ns_entries); + writeln!(out, "}} // namespace {}", namespace); } } @@ -128,10 +128,10 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: } } - for (child_ns, child_ns_entries) in ns_entries.nested_content() { - let block = format!("namespace {}", child_ns); + for (namespace, nested_ns_entries) in ns_entries.nested_content() { + let block = format!("namespace {}", namespace); out.begin_block(&block); - gen_namespace_contents(out, child_ns_entries, opt); + gen_namespace_contents(out, nested_ns_entries, opt); out.end_block(&block); } } From ef796967e88117d037b8e2d37c39ea9dcaa77504 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:01:33 +0000 Subject: [PATCH 1175/2232] Remove 'kids' naming --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index b4c8a93..ed3c91f 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -28,12 +28,12 @@ impl<'a> NamespaceEntries<'a> { nested: BTreeMap::new(), }; - let mut kids_by_child_ns = BTreeMap::new(); + let mut nested_namespaces = BTreeMap::new(); for api in apis { if let Some(ns) = api.namespace() { let first_ns_elem = ns.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { - let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + let list = nested_namespaces.entry(first_ns_elem).or_insert(Vec::new()); list.push(api); continue; } @@ -41,7 +41,7 @@ impl<'a> NamespaceEntries<'a> { root.direct.push(api); } - for (k, v) in kids_by_child_ns.into_iter() { + for (k, v) in nested_namespaces.into_iter() { root.nested .insert(k, Self::sort_by_inner_namespace(v, depth + 1)); } From 945c3cb9032b9ec239b2ce2ed9dd3fe35ed334cc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:05:01 +0000 Subject: [PATCH 1176/2232] Touch up sort_by_inner_namespace implementation --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index ed3c91f..16beba1 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -23,30 +23,28 @@ impl<'a> NamespaceEntries<'a> { } fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { - let mut root = NamespaceEntries { - direct: Vec::new(), - nested: BTreeMap::new(), - }; - + let mut direct = Vec::new(); let mut nested_namespaces = BTreeMap::new(); for api in apis { if let Some(ns) = api.namespace() { let first_ns_elem = ns.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { - let list = nested_namespaces.entry(first_ns_elem).or_insert(Vec::new()); - list.push(api); + nested_namespaces + .entry(first_ns_elem) + .or_insert_with(Vec::new) + .push(api); continue; } } - root.direct.push(api); + direct.push(api); } - for (k, v) in nested_namespaces.into_iter() { - root.nested - .insert(k, Self::sort_by_inner_namespace(v, depth + 1)); - } + let nested = nested_namespaces + .into_iter() + .map(|(k, apis)| (k, Self::sort_by_inner_namespace(apis, depth + 1))) + .collect(); - root + NamespaceEntries { direct, nested } } } From 73de7d0d527a2ae01810876a88d5ae9a3c468657 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:07:21 +0000 Subject: [PATCH 1177/2232] Detach sort_by_inner_namespace from NamespaceEntries --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index 16beba1..ce63de5 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -11,7 +11,7 @@ pub struct NamespaceEntries<'a> { impl<'a> NamespaceEntries<'a> { pub fn new(apis: &'a [Api]) -> Self { let api_refs = Vec::from_iter(apis); - Self::sort_by_inner_namespace(api_refs, 0) + sort_by_inner_namespace(api_refs, 0) } pub fn direct_content(&self) -> &[&'a Api] { @@ -21,31 +21,31 @@ impl<'a> NamespaceEntries<'a> { pub fn nested_content(&self) -> impl Iterator { self.nested.iter().map(|(k, entries)| (*k, entries)) } +} - fn sort_by_inner_namespace(apis: Vec<&'a Api>, depth: usize) -> Self { - let mut direct = Vec::new(); - let mut nested_namespaces = BTreeMap::new(); - for api in apis { - if let Some(ns) = api.namespace() { - let first_ns_elem = ns.iter().nth(depth); - if let Some(first_ns_elem) = first_ns_elem { - nested_namespaces - .entry(first_ns_elem) - .or_insert_with(Vec::new) - .push(api); - continue; - } +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut direct = Vec::new(); + let mut nested_namespaces = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + nested_namespaces + .entry(first_ns_elem) + .or_insert_with(Vec::new) + .push(api); + continue; } - direct.push(api); } + direct.push(api); + } - let nested = nested_namespaces - .into_iter() - .map(|(k, apis)| (k, Self::sort_by_inner_namespace(apis, depth + 1))) - .collect(); + let nested = nested_namespaces + .into_iter() + .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) + .collect(); - NamespaceEntries { direct, nested } - } + NamespaceEntries { direct, nested } } #[cfg(test)] From caab991cbcd96e3d62c965eb7101604526137a4e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:22:40 +0000 Subject: [PATCH 1178/2232] Clean up namespace_organizer unit test --- diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs index ce63de5..343acf7 100644 --- a/gen/src/namespace_organizer.rs +++ b/gen/src/namespace_organizer.rs @@ -58,7 +58,7 @@ mod tests { #[test] fn test_ns_entries_sort() { - let entries = vec![ + let apis = &[ make_api(None, "C"), make_api(None, "A"), make_api(Some("G"), "E"), @@ -70,58 +70,66 @@ mod tests { make_api(Some("D"), "I"), make_api(Some("D"), "J"), ]; - let ns = NamespaceEntries::new(&entries); - let root_entries = ns.direct_content(); - assert_eq!(root_entries.len(), 3); - assert_ident(root_entries[0], "C"); - assert_ident(root_entries[1], "A"); - assert_ident(root_entries[2], "B"); - let mut kids = ns.nested_content(); - let (d_id, d_nse) = kids.next().unwrap(); - assert_eq!(d_id.to_string(), "D"); - let (g_id, g_nse) = kids.next().unwrap(); - assert_eq!(g_id.to_string(), "G"); - assert!(kids.next().is_none()); - let d_nse_entries = d_nse.direct_content(); - assert_eq!(d_nse_entries.len(), 3); - assert_ident(d_nse_entries[0], "F"); - assert_ident(d_nse_entries[1], "I"); - assert_ident(d_nse_entries[2], "J"); - let g_nse_entries = g_nse.direct_content(); - assert_eq!(g_nse_entries.len(), 2); - assert_ident(g_nse_entries[0], "E"); - assert_ident(g_nse_entries[1], "H"); - let mut g_kids = g_nse.nested_content(); - assert!(g_kids.next().is_none()); - let mut d_kids = d_nse.nested_content(); - let (k_id, k_nse) = d_kids.next().unwrap(); - assert_eq!(k_id.to_string(), "K"); - let k_nse_entries = k_nse.direct_content(); - assert_eq!(k_nse_entries.len(), 2); - assert_ident(k_nse_entries[0], "L"); - assert_ident(k_nse_entries[1], "M"); + + let root = NamespaceEntries::new(apis); + + // :: + let root_direct = root.direct_content(); + assert_eq!(root_direct.len(), 3); + assert_ident(root_direct[0], "C"); + assert_ident(root_direct[1], "A"); + assert_ident(root_direct[2], "B"); + + let mut root_nested = root.nested_content(); + let (id, d) = root_nested.next().unwrap(); + assert_eq!(id, "D"); + let (id, g) = root_nested.next().unwrap(); + assert_eq!(id, "G"); + assert!(root_nested.next().is_none()); + + // ::D + let d_direct = d.direct_content(); + assert_eq!(d_direct.len(), 3); + assert_ident(d_direct[0], "F"); + assert_ident(d_direct[1], "I"); + assert_ident(d_direct[2], "J"); + + let mut d_nested = d.nested_content(); + let (id, k) = d_nested.next().unwrap(); + assert_eq!(id, "K"); + + // ::D::K + let k_direct = k.direct_content(); + assert_eq!(k_direct.len(), 2); + assert_ident(k_direct[0], "L"); + assert_ident(k_direct[1], "M"); + + // ::G + let g_direct = g.direct_content(); + assert_eq!(g_direct.len(), 2); + assert_ident(g_direct[0], "E"); + assert_ident(g_direct[1], "H"); + + let mut g_nested = g.nested_content(); + assert!(g_nested.next().is_none()); } fn assert_ident(api: &Api, expected: &str) { if let Api::CxxType(cxx_type) = api { - assert_eq!(cxx_type.ident.cxx.ident.to_string(), expected); + assert_eq!(cxx_type.ident.cxx.ident, expected); } else { unreachable!() } } fn make_api(ns: Option<&str>, ident: &str) -> Api { - let ns = match ns { - Some(st) => Namespace::from_str(st), - None => Namespace::none(), - }; - let ident = Pair::new(ns, Ident::new(ident, Span::call_site())); + let ns = ns.map_or_else(Namespace::none, Namespace::from_str); Api::CxxType(ExternType { doc: Doc::new(), type_token: Token![type](Span::call_site()), - ident, + ident: Pair::new(ns, Ident::new(ident, Span::call_site())), semi_token: Token![;](Span::call_site()), - trusted: true, + trusted: false, }) } } From bd9608fd66fbbcf974799e3f95539962cd60683a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:24:19 +0000 Subject: [PATCH 1179/2232] Move namespace sorter to alphasort module To make room for an upcoming topological sorter in a toposort module. --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs new file mode 100644 index 0000000..343acf7 --- /dev/null +++ b/gen/src/alphasort.rs @@ -0,0 +1,135 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::BTreeMap; +use std::iter::FromIterator; + +pub struct NamespaceEntries<'a> { + direct: Vec<&'a Api>, + nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, +} + +impl<'a> NamespaceEntries<'a> { + pub fn new(apis: &'a [Api]) -> Self { + let api_refs = Vec::from_iter(apis); + sort_by_inner_namespace(api_refs, 0) + } + + pub fn direct_content(&self) -> &[&'a Api] { + &self.direct + } + + pub fn nested_content(&self) -> impl Iterator { + self.nested.iter().map(|(k, entries)| (*k, entries)) + } +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut direct = Vec::new(); + let mut nested_namespaces = BTreeMap::new(); + for api in apis { + if let Some(ns) = api.namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + nested_namespaces + .entry(first_ns_elem) + .or_insert_with(Vec::new) + .push(api); + continue; + } + } + direct.push(api); + } + + let nested = nested_namespaces + .into_iter() + .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) + .collect(); + + NamespaceEntries { direct, nested } +} + +#[cfg(test)] +mod tests { + use super::NamespaceEntries; + use crate::syntax::namespace::Namespace; + use crate::syntax::{Api, Doc, ExternType, Pair}; + use proc_macro2::{Ident, Span}; + use syn::Token; + + #[test] + fn test_ns_entries_sort() { + let apis = &[ + make_api(None, "C"), + make_api(None, "A"), + make_api(Some("G"), "E"), + make_api(Some("D"), "F"), + make_api(Some("G"), "H"), + make_api(Some("D::K"), "L"), + make_api(Some("D::K"), "M"), + make_api(None, "B"), + make_api(Some("D"), "I"), + make_api(Some("D"), "J"), + ]; + + let root = NamespaceEntries::new(apis); + + // :: + let root_direct = root.direct_content(); + assert_eq!(root_direct.len(), 3); + assert_ident(root_direct[0], "C"); + assert_ident(root_direct[1], "A"); + assert_ident(root_direct[2], "B"); + + let mut root_nested = root.nested_content(); + let (id, d) = root_nested.next().unwrap(); + assert_eq!(id, "D"); + let (id, g) = root_nested.next().unwrap(); + assert_eq!(id, "G"); + assert!(root_nested.next().is_none()); + + // ::D + let d_direct = d.direct_content(); + assert_eq!(d_direct.len(), 3); + assert_ident(d_direct[0], "F"); + assert_ident(d_direct[1], "I"); + assert_ident(d_direct[2], "J"); + + let mut d_nested = d.nested_content(); + let (id, k) = d_nested.next().unwrap(); + assert_eq!(id, "K"); + + // ::D::K + let k_direct = k.direct_content(); + assert_eq!(k_direct.len(), 2); + assert_ident(k_direct[0], "L"); + assert_ident(k_direct[1], "M"); + + // ::G + let g_direct = g.direct_content(); + assert_eq!(g_direct.len(), 2); + assert_ident(g_direct[0], "E"); + assert_ident(g_direct[1], "H"); + + let mut g_nested = g.nested_content(); + assert!(g_nested.next().is_none()); + } + + fn assert_ident(api: &Api, expected: &str) { + if let Api::CxxType(cxx_type) = api { + assert_eq!(cxx_type.ident.cxx.ident, expected); + } else { + unreachable!() + } + } + + fn make_api(ns: Option<&str>, ident: &str) -> Api { + let ns = ns.map_or_else(Namespace::none, Namespace::from_str); + Api::CxxType(ExternType { + doc: Doc::new(), + type_token: Token![type](Span::call_site()), + ident: Pair::new(ns, Ident::new(ident, Span::call_site())), + semi_token: Token![;](Span::call_site()), + trusted: false, + }) + } +} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 453f47b..2cd4e56 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,6 +1,7 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. +mod alphasort; mod builtin; mod check; pub(super) mod error; @@ -8,7 +9,6 @@ mod file; pub(super) mod fs; mod ifndef; pub(super) mod include; -mod namespace_organizer; pub(super) mod out; mod write; diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs deleted file mode 100644 index 343acf7..0000000 --- a/gen/src/namespace_organizer.rs +++ /dev/null @@ -1,135 +0,0 @@ -use crate::syntax::Api; -use proc_macro2::Ident; -use std::collections::BTreeMap; -use std::iter::FromIterator; - -pub struct NamespaceEntries<'a> { - direct: Vec<&'a Api>, - nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, -} - -impl<'a> NamespaceEntries<'a> { - pub fn new(apis: &'a [Api]) -> Self { - let api_refs = Vec::from_iter(apis); - sort_by_inner_namespace(api_refs, 0) - } - - pub fn direct_content(&self) -> &[&'a Api] { - &self.direct - } - - pub fn nested_content(&self) -> impl Iterator { - self.nested.iter().map(|(k, entries)| (*k, entries)) - } -} - -fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { - let mut direct = Vec::new(); - let mut nested_namespaces = BTreeMap::new(); - for api in apis { - if let Some(ns) = api.namespace() { - let first_ns_elem = ns.iter().nth(depth); - if let Some(first_ns_elem) = first_ns_elem { - nested_namespaces - .entry(first_ns_elem) - .or_insert_with(Vec::new) - .push(api); - continue; - } - } - direct.push(api); - } - - let nested = nested_namespaces - .into_iter() - .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) - .collect(); - - NamespaceEntries { direct, nested } -} - -#[cfg(test)] -mod tests { - use super::NamespaceEntries; - use crate::syntax::namespace::Namespace; - use crate::syntax::{Api, Doc, ExternType, Pair}; - use proc_macro2::{Ident, Span}; - use syn::Token; - - #[test] - fn test_ns_entries_sort() { - let apis = &[ - make_api(None, "C"), - make_api(None, "A"), - make_api(Some("G"), "E"), - make_api(Some("D"), "F"), - make_api(Some("G"), "H"), - make_api(Some("D::K"), "L"), - make_api(Some("D::K"), "M"), - make_api(None, "B"), - make_api(Some("D"), "I"), - make_api(Some("D"), "J"), - ]; - - let root = NamespaceEntries::new(apis); - - // :: - let root_direct = root.direct_content(); - assert_eq!(root_direct.len(), 3); - assert_ident(root_direct[0], "C"); - assert_ident(root_direct[1], "A"); - assert_ident(root_direct[2], "B"); - - let mut root_nested = root.nested_content(); - let (id, d) = root_nested.next().unwrap(); - assert_eq!(id, "D"); - let (id, g) = root_nested.next().unwrap(); - assert_eq!(id, "G"); - assert!(root_nested.next().is_none()); - - // ::D - let d_direct = d.direct_content(); - assert_eq!(d_direct.len(), 3); - assert_ident(d_direct[0], "F"); - assert_ident(d_direct[1], "I"); - assert_ident(d_direct[2], "J"); - - let mut d_nested = d.nested_content(); - let (id, k) = d_nested.next().unwrap(); - assert_eq!(id, "K"); - - // ::D::K - let k_direct = k.direct_content(); - assert_eq!(k_direct.len(), 2); - assert_ident(k_direct[0], "L"); - assert_ident(k_direct[1], "M"); - - // ::G - let g_direct = g.direct_content(); - assert_eq!(g_direct.len(), 2); - assert_ident(g_direct[0], "E"); - assert_ident(g_direct[1], "H"); - - let mut g_nested = g.nested_content(); - assert!(g_nested.next().is_none()); - } - - fn assert_ident(api: &Api, expected: &str) { - if let Api::CxxType(cxx_type) = api { - assert_eq!(cxx_type.ident.cxx.ident, expected); - } else { - unreachable!() - } - } - - fn make_api(ns: Option<&str>, ident: &str) -> Api { - let ns = ns.map_or_else(Namespace::none, Namespace::from_str); - Api::CxxType(ExternType { - doc: Doc::new(), - type_token: Token![type](Span::call_site()), - ident: Pair::new(ns, Ident::new(ident, Span::call_site())), - semi_token: Token![;](Span::call_site()), - trusted: false, - }) - } -} diff --git a/gen/src/write.rs b/gen/src/write.rs index 9f57f7d..e857d21 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,4 @@ -use crate::gen::namespace_organizer::NamespaceEntries; +use crate::gen::alphasort::NamespaceEntries; use crate::gen::out::OutFile; use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; From e1476af7615b4cf7cf6a2810f75e5a13dad9f55c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:49:04 +0000 Subject: [PATCH 1180/2232] Make Opt accessible via OutFile --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 337bc93..6ba44aa 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,11 +1,13 @@ use crate::gen::builtin::Builtins; use crate::gen::include::Includes; +use crate::gen::Opt; use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile<'a> { pub header: bool, + pub opt: &'a Opt, pub types: &'a Types<'a>, pub include: Includes, pub builtin: Builtins, @@ -20,9 +22,10 @@ pub struct Content { } impl<'a> OutFile<'a> { - pub fn new(header: bool, types: &'a Types) -> Self { + pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self { OutFile { header, + opt, types, include: Includes::new(), builtin: Builtins::new(), diff --git a/gen/src/write.rs b/gen/src/write.rs index e857d21..dcaec40 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -10,8 +10,8 @@ use crate::syntax::{ use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -> OutFile<'a> { - let mut out_file = OutFile::new(header, types); +pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &'a Opt, header: bool) -> OutFile<'a> { + let mut out_file = OutFile::new(header, opt, types); let out = &mut out_file; if header { @@ -23,7 +23,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) - let apis_by_namespace = NamespaceEntries::new(apis); gen_namespace_forward_declarations(out, &apis_by_namespace); - gen_namespace_contents(out, &apis_by_namespace, opt); + gen_namespace_contents(out, &apis_by_namespace); if !header { out.next_section(); @@ -56,7 +56,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } -fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: &Opt) { +fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -110,13 +110,13 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { - let (efn, write): (_, fn(_, _, _)) = match api { + let (efn, write): (_, fn(_, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), Api::RustFunction(efn) => (efn, write_rust_function_decl), _ => continue, }; out.next_section(); - write(out, efn, &opt.cxx_impl_annotations); + write(out, efn); } out.end_block("extern \"C\""); } @@ -131,7 +131,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: for (namespace, nested_ns_entries) in ns_entries.nested_content() { let block = format!("namespace {}", namespace); out.begin_block(&block); - gen_namespace_contents(out, nested_ns_entries, opt); + gen_namespace_contents(out, nested_ns_entries); out.end_block(&block); } } @@ -343,8 +343,8 @@ fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { } } -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option) { - if let Some(annotation) = impl_annotations { +fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { + if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); } if efn.throws { @@ -542,7 +542,7 @@ fn write_function_pointer_trampoline( write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call); } -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, _: &Option) { +fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn) { let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, indirect_call); From 047707440c17d07512b53bfd7561fb48e8279d42 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 21:52:18 +0000 Subject: [PATCH 1181/2232] Simplify gen_namespace_contents' extern block code --- diff --git a/gen/src/write.rs b/gen/src/write.rs index dcaec40..c008fcb 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -110,13 +110,11 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { out.begin_block("extern \"C\""); write_exception_glue(out, apis); for api in apis { - let (efn, write): (_, fn(_, _)) = match api { - Api::CxxFunction(efn) => (efn, write_cxx_function_shim), - Api::RustFunction(efn) => (efn, write_rust_function_decl), - _ => continue, - }; - out.next_section(); - write(out, efn); + match api { + Api::CxxFunction(efn) => write_cxx_function_shim(out, efn), + Api::RustFunction(efn) => write_rust_function_decl(out, efn), + _ => {} + } } out.end_block("extern \"C\""); } @@ -344,6 +342,7 @@ fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { } fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { + out.next_section(); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); } @@ -532,7 +531,6 @@ fn write_function_pointer_trampoline( var: &Ident, f: &Signature, ) { - out.next_section(); let r_trampoline = mangle::r_trampoline(efn, var, out.types); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call); @@ -554,6 +552,7 @@ fn write_rust_function_decl_impl( sig: &Signature, indirect_call: bool, ) { + out.next_section(); if sig.throws { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); From ce7b37ccb7508831a67575ec85b20ce5e3686bb2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 22:09:07 +0000 Subject: [PATCH 1182/2232] Remove unneeded cfg test stringified Namespace parser --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 343acf7..8d67d07 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -123,7 +123,7 @@ mod tests { } fn make_api(ns: Option<&str>, ident: &str) -> Api { - let ns = ns.map_or_else(Namespace::none, Namespace::from_str); + let ns = ns.map_or_else(Namespace::none, |ns| syn::parse_str(ns).unwrap()); Api::CxxType(ExternType { doc: Doc::new(), type_token: Token![type](Span::call_site()), diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 4a37f7a..a9ef43c 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,7 +1,5 @@ use crate::syntax::qualified::QualifiedName; use crate::syntax::Api; -#[cfg(test)] -use proc_macro2::Span; use quote::IdentFragment; use std::fmt::{self, Display}; use std::slice::Iter; @@ -39,16 +37,6 @@ impl Namespace { input.parse::>()?; Ok(ns) } - - #[cfg(test)] - pub fn from_str(ns: &str) -> Self { - Namespace { - segments: ns - .split("::") - .map(|x| Ident::new(x, Span::call_site())) - .collect(), - } - } } impl Parse for Namespace { From 4ebde54fd38d5a635ae927287ec86e2a8fb746ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 01 2020 22:24:11 +0000 Subject: [PATCH 1183/2232] Rename Namespace::none() to const Namespace::ROOT --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 8d67d07..12251d5 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -123,7 +123,7 @@ mod tests { } fn make_api(ns: Option<&str>, ident: &str) -> Api { - let ns = ns.map_or_else(Namespace::none, |ns| syn::parse_str(ns).unwrap()); + let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap()); Api::CxxType(ExternType { doc: Doc::new(), type_token: Token![type](Span::call_site()), diff --git a/gen/src/file.rs b/gen/src/file.rs index 1b324cb..46616fb 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -20,7 +20,7 @@ impl Parse for File { fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { while !input.is_empty() { let mut cxx_bridge = false; - let mut namespace = Namespace::none(); + let mut namespace = Namespace::ROOT; let mut attrs = input.call(Attribute::parse_outer)?; for attr in &attrs { let path = &attr.path.segments; @@ -65,7 +65,7 @@ fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { fn parse_args(attr: &Attribute) -> Result { if attr.tokens.is_empty() { - Ok(Namespace::none()) + Ok(Namespace::ROOT) } else { attr.parse_args_with(Namespace::parse_bridge_attr_namespace) } diff --git a/syntax/file.rs b/syntax/file.rs index 931ce6e..33340d8 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -36,7 +36,7 @@ pub struct ItemForeignMod { impl Parse for Module { fn parse(input: ParseStream) -> Result { - let namespace = Namespace::none(); + let namespace = Namespace::ROOT; let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let unsafety: Option = input.parse()?; diff --git a/syntax/namespace.rs b/syntax/namespace.rs index a9ef43c..834a1d5 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -16,11 +16,9 @@ pub struct Namespace { } impl Namespace { - pub fn none() -> Self { - Namespace { - segments: Vec::new(), - } - } + pub const ROOT: Self = Namespace { + segments: Vec::new(), + }; pub fn iter(&self) -> Iter { self.segments.iter() @@ -28,7 +26,7 @@ impl Namespace { pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result { if input.is_empty() { - return Ok(Namespace::none()); + return Ok(Namespace::ROOT); } input.parse::()?; From 98565a2535b9246c677d131b0972b199904cdb88 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 00:52:08 +0000 Subject: [PATCH 1184/2232] Keep apis usable later in sort_by_inner_namespace --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 12251d5..ea911f8 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -26,18 +26,18 @@ impl<'a> NamespaceEntries<'a> { fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut direct = Vec::new(); let mut nested_namespaces = BTreeMap::new(); - for api in apis { + for api in &apis { if let Some(ns) = api.namespace() { let first_ns_elem = ns.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { nested_namespaces .entry(first_ns_elem) .or_insert_with(Vec::new) - .push(api); + .push(*api); continue; } } - direct.push(api); + direct.push(*api); } let nested = nested_namespaces From ac7188c8717e7aeb1535e876682e5038dc0ffcc7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 00:58:16 +0000 Subject: [PATCH 1185/2232] Eliminate need for lifetimes in write::gen signature --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2cd4e56..554334c 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -136,12 +136,12 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { // one or the other. Ok(GeneratedCode { header: if opt.gen_header { - write::gen(apis, types, opt, true).content() + write::gen(apis, types, opt, true) } else { Vec::new() }, implementation: if opt.gen_implementation { - write::gen(apis, types, opt, false).content() + write::gen(apis, types, opt, false) } else { Vec::new() }, diff --git a/gen/src/write.rs b/gen/src/write.rs index c008fcb..57c699c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -10,7 +10,7 @@ use crate::syntax::{ use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &'a Opt, header: bool) -> OutFile<'a> { +pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec { let mut out_file = OutFile::new(header, opt, types); let out = &mut out_file; @@ -33,7 +33,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &'a Opt, header: bool builtin::write(out); include::write(out); - out_file + out_file.content() } fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) { From 0c033e39173f1b563a664c965c55f75ab9b07689 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 01:31:56 +0000 Subject: [PATCH 1186/2232] Preserve meaning of block boundaries --- diff --git a/gen/src/block.rs b/gen/src/block.rs new file mode 100644 index 0000000..e7f7c63 --- /dev/null +++ b/gen/src/block.rs @@ -0,0 +1,44 @@ +use proc_macro2::Ident; + +pub enum Block { + AnonymousNamespace, + Namespace(&'static str), + UserDefinedNamespace(Ident), + InlineNamespace(&'static str), + ExternC, +} + +impl Block { + pub fn write_begin(&self, out: &mut String) { + if let Block::InlineNamespace(_) = self { + out.push_str("inline "); + } + self.write_common(out); + out.push_str(" {\n"); + } + + pub fn write_end(&self, out: &mut String) { + out.push_str("} // "); + self.write_common(out); + out.push('\n'); + } + + fn write_common(&self, out: &mut String) { + match self { + Block::AnonymousNamespace => out.push_str("namespace"), + Block::Namespace(name) => { + out.push_str("namespace "); + out.push_str(name); + } + Block::UserDefinedNamespace(name) => { + out.push_str("namespace "); + out.push_str(&name.to_string()); + } + Block::InlineNamespace(name) => { + out.push_str("namespace "); + out.push_str(name); + } + Block::ExternC => out.push_str("extern \"C\""), + } + } +} diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 448b6f8..e755e10 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -1,3 +1,4 @@ +use crate::gen::block::Block; use crate::gen::ifndef; use crate::gen::out::{Content, OutFile}; @@ -39,8 +40,8 @@ pub(super) fn write(out: &mut OutFile) { let builtin = &mut out.builtin; let out = &mut builtin.content; - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); + out.begin_block(Block::Namespace("rust")); + out.begin_block(Block::InlineNamespace("cxxbridge05")); writeln!(out, "// #include \"rust/cxx.h\""); ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); @@ -51,10 +52,10 @@ pub(super) fn write(out: &mut OutFile) { } if builtin.rust_error { - out.begin_block("namespace"); + out.begin_block(Block::AnonymousNamespace); writeln!(out, "template "); writeln!(out, "class impl;"); - out.end_block("namespace"); + out.end_block(Block::AnonymousNamespace); } ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); @@ -91,15 +92,15 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } - out.begin_block("namespace"); + out.begin_block(Block::AnonymousNamespace); if builtin.ptr_len { - out.begin_block("namespace repr"); + out.begin_block(Block::Namespace("repr")); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); writeln!(out, " size_t len;"); writeln!(out, "}};"); - out.end_block("namespace repr"); + out.end_block(Block::Namespace("repr")); } if builtin.rust_str_new_unchecked || builtin.rust_str_repr { @@ -167,11 +168,11 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } - out.end_block("namespace"); - out.end_block("namespace cxxbridge05"); + out.end_block(Block::AnonymousNamespace); + out.end_block(Block::InlineNamespace("cxxbridge05")); if builtin.trycatch { - out.begin_block("namespace behavior"); + out.begin_block(Block::Namespace("behavior")); include.exception = true; include.type_traits = true; include.utility = true; @@ -190,8 +191,8 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}} catch (const ::std::exception &e) {{"); writeln!(out, " fail(e.what());"); writeln!(out, "}}"); - out.end_block("namespace behavior"); + out.end_block(Block::Namespace("behavior")); } - out.end_block("namespace rust"); + out.end_block(Block::Namespace("rust")); } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a4a159d..8626058 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,6 +2,7 @@ // the cxxbridge CLI command. mod alphasort; +mod block; mod builtin; mod check; pub(super) mod error; diff --git a/gen/src/out.rs b/gen/src/out.rs index 6ba44aa..606a586 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,3 +1,4 @@ +use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::Opt; @@ -18,7 +19,7 @@ pub(crate) struct OutFile<'a> { pub struct Content { bytes: String, section_pending: bool, - blocks_pending: Vec, + blocks_pending: Vec, } impl<'a> OutFile<'a> { @@ -38,11 +39,11 @@ impl<'a> OutFile<'a> { self.content.get_mut().next_section(); } - pub fn begin_block(&mut self, block: &str) { + pub fn begin_block(&mut self, block: Block) { self.content.get_mut().begin_block(block); } - pub fn end_block(&mut self, block: &str) { + pub fn end_block(&mut self, block: Block) { self.content.get_mut().end_block(block); } @@ -95,15 +96,13 @@ impl Content { self.section_pending = true; } - pub fn begin_block(&mut self, block: &str) { - self.blocks_pending.push(block.to_owned()); + pub fn begin_block(&mut self, block: Block) { + self.blocks_pending.push(block); } - pub fn end_block(&mut self, block: &str) { + pub fn end_block(&mut self, block: Block) { if self.blocks_pending.pop().is_none() { - self.bytes.push_str("} // "); - self.bytes.push_str(block); - self.bytes.push('\n'); + Block::write_end(&block, &mut self.bytes); self.section_pending = true; } } @@ -119,8 +118,7 @@ impl Content { self.bytes.push('\n'); } for block in self.blocks_pending.drain(..) { - self.bytes.push_str(&block); - self.bytes.push_str(" {\n"); + Block::write_begin(&block, &mut self.bytes); } self.section_pending = false; } else if self.section_pending { diff --git a/gen/src/write.rs b/gen/src/write.rs index 57c699c..a1f168f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,5 @@ use crate::gen::alphasort::NamespaceEntries; +use crate::gen::block::Block; use crate::gen::out::OutFile; use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -107,7 +108,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } if !out.header { - out.begin_block("extern \"C\""); + out.begin_block(Block::ExternC); write_exception_glue(out, apis); for api in apis { match api { @@ -116,7 +117,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { _ => {} } } - out.end_block("extern \"C\""); + out.end_block(Block::ExternC); } for api in apis { @@ -127,10 +128,9 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } for (namespace, nested_ns_entries) in ns_entries.nested_content() { - let block = format!("namespace {}", namespace); - out.begin_block(&block); + out.begin_block(Block::UserDefinedNamespace(namespace.clone())); gen_namespace_contents(out, nested_ns_entries); - out.end_block(&block); + out.end_block(Block::UserDefinedNamespace(namespace.clone())); } } @@ -973,7 +973,7 @@ fn to_mangled(ty: &Type, types: &Types) -> Symbol { } fn write_generic_instantiations(out: &mut OutFile) { - out.begin_block("extern \"C\""); + out.begin_block(Block::ExternC); for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1009,10 +1009,10 @@ fn write_generic_instantiations(out: &mut OutFile) { } } } - out.end_block("extern \"C\""); + out.end_block(Block::ExternC); - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); + out.begin_block(Block::Namespace("rust")); + out.begin_block(Block::InlineNamespace("cxxbridge05")); for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1026,8 +1026,8 @@ fn write_generic_instantiations(out: &mut OutFile) { } } } - out.end_block("namespace cxxbridge05"); - out.end_block("namespace rust"); + out.end_block(Block::InlineNamespace("cxxbridge05")); + out.end_block(Block::Namespace("rust")); } fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { From 92b7b6d5757bb9a80424cb60a2b87e09e52b04a8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 01:31:56 +0000 Subject: [PATCH 1187/2232] Condense creation of GeneratedCode --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 554334c..a4a159d 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -134,16 +134,15 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { // Some callers may wish to generate both header and implementation from the // same token stream to avoid parsing twice. Others only need to generate // one or the other. + let (mut header, mut implementation) = Default::default(); + if opt.gen_header { + header = write::gen(apis, types, opt, true); + } + if opt.gen_implementation { + implementation = write::gen(apis, types, opt, false); + } Ok(GeneratedCode { - header: if opt.gen_header { - write::gen(apis, types, opt, true) - } else { - Vec::new() - }, - implementation: if opt.gen_implementation { - write::gen(apis, types, opt, false) - } else { - Vec::new() - }, + header, + implementation, }) } From 97c5b86913bc7c5dd53ca4af8bb8a300fc638257 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 01:31:56 +0000 Subject: [PATCH 1188/2232] Borrow syntax tree idents to make Block copyable --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index ea911f8..95b49a6 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -18,7 +18,7 @@ impl<'a> NamespaceEntries<'a> { &self.direct } - pub fn nested_content(&self) -> impl Iterator { + pub fn nested_content(&self) -> impl Iterator)> { self.nested.iter().map(|(k, entries)| (*k, entries)) } } diff --git a/gen/src/block.rs b/gen/src/block.rs index e7f7c63..7775c52 100644 --- a/gen/src/block.rs +++ b/gen/src/block.rs @@ -1,14 +1,15 @@ use proc_macro2::Ident; -pub enum Block { +#[derive(Copy, Clone)] +pub enum Block<'a> { AnonymousNamespace, Namespace(&'static str), - UserDefinedNamespace(Ident), + UserDefinedNamespace(&'a Ident), InlineNamespace(&'static str), ExternC, } -impl Block { +impl<'a> Block<'a> { pub fn write_begin(&self, out: &mut String) { if let Block::InlineNamespace(_) = self { out.push_str("inline "); diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index e755e10..4b8dce5 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -3,7 +3,7 @@ use crate::gen::ifndef; use crate::gen::out::{Content, OutFile}; #[derive(Default, PartialEq)] -pub struct Builtins { +pub struct Builtins<'a> { pub panic: bool, pub rust_string: bool, pub rust_str: bool, @@ -22,10 +22,10 @@ pub struct Builtins { pub rust_str_repr: bool, pub rust_slice_new: bool, pub rust_slice_repr: bool, - pub content: Content, + pub content: Content<'a>, } -impl Builtins { +impl<'a> Builtins<'a> { pub fn new() -> Self { Builtins::default() } diff --git a/gen/src/include.rs b/gen/src/include.rs index 8dc7146..d88c12d 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -19,7 +19,7 @@ pub struct Include { } #[derive(Default, PartialEq)] -pub struct Includes { +pub struct Includes<'a> { pub custom: Vec, pub array: bool, pub cstddef: bool, @@ -33,10 +33,10 @@ pub struct Includes { pub utility: bool, pub vector: bool, pub basetsd: bool, - pub content: Content, + pub content: Content<'a>, } -impl Includes { +impl<'a> Includes<'a> { pub fn new() -> Self { Includes::default() } @@ -101,13 +101,13 @@ pub(super) fn write(out: &mut OutFile) { } } -impl<'a> Extend<&'a Include> for Includes { - fn extend>(&mut self, iter: I) { +impl<'i, 'a> Extend<&'i Include> for Includes<'a> { + fn extend>(&mut self, iter: I) { self.custom.extend(iter.into_iter().cloned()); } } -impl<'a> From<&'a syntax::Include> for Include { +impl<'i> From<&'i syntax::Include> for Include { fn from(include: &syntax::Include) -> Self { Include { path: include.path.clone(), @@ -116,15 +116,15 @@ impl<'a> From<&'a syntax::Include> for Include { } } -impl Deref for Includes { - type Target = Content; +impl<'a> Deref for Includes<'a> { + type Target = Content<'a>; fn deref(&self) -> &Self::Target { &self.content } } -impl DerefMut for Includes { +impl<'a> DerefMut for Includes<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.content } diff --git a/gen/src/out.rs b/gen/src/out.rs index 606a586..6867a76 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -10,16 +10,16 @@ pub(crate) struct OutFile<'a> { pub header: bool, pub opt: &'a Opt, pub types: &'a Types<'a>, - pub include: Includes, - pub builtin: Builtins, - content: RefCell, + pub include: Includes<'a>, + pub builtin: Builtins<'a>, + content: RefCell>, } #[derive(Default)] -pub struct Content { +pub struct Content<'a> { bytes: String, section_pending: bool, - blocks_pending: Vec, + blocks_pending: Vec>, } impl<'a> OutFile<'a> { @@ -39,11 +39,11 @@ impl<'a> OutFile<'a> { self.content.get_mut().next_section(); } - pub fn begin_block(&mut self, block: Block) { + pub fn begin_block(&mut self, block: Block<'a>) { self.content.get_mut().begin_block(block); } - pub fn end_block(&mut self, block: Block) { + pub fn end_block(&mut self, block: Block<'a>) { self.content.get_mut().end_block(block); } @@ -74,20 +74,20 @@ impl<'a> OutFile<'a> { } } -impl Write for Content { +impl<'a> Write for Content<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { self.write(s); Ok(()) } } -impl PartialEq for Content { +impl<'a> PartialEq for Content<'a> { fn eq(&self, _other: &Content) -> bool { true } } -impl Content { +impl<'a> Content<'a> { fn new() -> Self { Content::default() } @@ -96,11 +96,11 @@ impl Content { self.section_pending = true; } - pub fn begin_block(&mut self, block: Block) { + pub fn begin_block(&mut self, block: Block<'a>) { self.blocks_pending.push(block); } - pub fn end_block(&mut self, block: Block) { + pub fn end_block(&mut self, block: Block<'a>) { if self.blocks_pending.pop().is_none() { Block::write_end(&block, &mut self.bytes); self.section_pending = true; diff --git a/gen/src/write.rs b/gen/src/write.rs index a1f168f..0b6a5aa 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -57,7 +57,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } -fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { +fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntries<'a>) { let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -128,9 +128,10 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } for (namespace, nested_ns_entries) in ns_entries.nested_content() { - out.begin_block(Block::UserDefinedNamespace(namespace.clone())); + let block = Block::UserDefinedNamespace(namespace); + out.begin_block(block); gen_namespace_contents(out, nested_ns_entries); - out.end_block(Block::UserDefinedNamespace(namespace.clone())); + out.end_block(block); } } From 8da98ca66b38af411a93d967fd92cf9ed8e77191 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 01:43:57 +0000 Subject: [PATCH 1189/2232] Merge pull request #399 from dtolnay/block Preserve meaning of block boundaries --- diff --git a/gen/src/block.rs b/gen/src/block.rs new file mode 100644 index 0000000..e7f7c63 --- /dev/null +++ b/gen/src/block.rs @@ -0,0 +1,44 @@ +use proc_macro2::Ident; + +pub enum Block { + AnonymousNamespace, + Namespace(&'static str), + UserDefinedNamespace(Ident), + InlineNamespace(&'static str), + ExternC, +} + +impl Block { + pub fn write_begin(&self, out: &mut String) { + if let Block::InlineNamespace(_) = self { + out.push_str("inline "); + } + self.write_common(out); + out.push_str(" {\n"); + } + + pub fn write_end(&self, out: &mut String) { + out.push_str("} // "); + self.write_common(out); + out.push('\n'); + } + + fn write_common(&self, out: &mut String) { + match self { + Block::AnonymousNamespace => out.push_str("namespace"), + Block::Namespace(name) => { + out.push_str("namespace "); + out.push_str(name); + } + Block::UserDefinedNamespace(name) => { + out.push_str("namespace "); + out.push_str(&name.to_string()); + } + Block::InlineNamespace(name) => { + out.push_str("namespace "); + out.push_str(name); + } + Block::ExternC => out.push_str("extern \"C\""), + } + } +} diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 448b6f8..e755e10 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -1,3 +1,4 @@ +use crate::gen::block::Block; use crate::gen::ifndef; use crate::gen::out::{Content, OutFile}; @@ -39,8 +40,8 @@ pub(super) fn write(out: &mut OutFile) { let builtin = &mut out.builtin; let out = &mut builtin.content; - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); + out.begin_block(Block::Namespace("rust")); + out.begin_block(Block::InlineNamespace("cxxbridge05")); writeln!(out, "// #include \"rust/cxx.h\""); ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC"); @@ -51,10 +52,10 @@ pub(super) fn write(out: &mut OutFile) { } if builtin.rust_error { - out.begin_block("namespace"); + out.begin_block(Block::AnonymousNamespace); writeln!(out, "template "); writeln!(out, "class impl;"); - out.end_block("namespace"); + out.end_block(Block::AnonymousNamespace); } ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING"); @@ -91,15 +92,15 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } - out.begin_block("namespace"); + out.begin_block(Block::AnonymousNamespace); if builtin.ptr_len { - out.begin_block("namespace repr"); + out.begin_block(Block::Namespace("repr")); writeln!(out, "struct PtrLen final {{"); writeln!(out, " const void *ptr;"); writeln!(out, " size_t len;"); writeln!(out, "}};"); - out.end_block("namespace repr"); + out.end_block(Block::Namespace("repr")); } if builtin.rust_str_new_unchecked || builtin.rust_str_repr { @@ -167,11 +168,11 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } - out.end_block("namespace"); - out.end_block("namespace cxxbridge05"); + out.end_block(Block::AnonymousNamespace); + out.end_block(Block::InlineNamespace("cxxbridge05")); if builtin.trycatch { - out.begin_block("namespace behavior"); + out.begin_block(Block::Namespace("behavior")); include.exception = true; include.type_traits = true; include.utility = true; @@ -190,8 +191,8 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}} catch (const ::std::exception &e) {{"); writeln!(out, " fail(e.what());"); writeln!(out, "}}"); - out.end_block("namespace behavior"); + out.end_block(Block::Namespace("behavior")); } - out.end_block("namespace rust"); + out.end_block(Block::Namespace("rust")); } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index a4a159d..8626058 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,6 +2,7 @@ // the cxxbridge CLI command. mod alphasort; +mod block; mod builtin; mod check; pub(super) mod error; diff --git a/gen/src/out.rs b/gen/src/out.rs index 6ba44aa..606a586 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,3 +1,4 @@ +use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::Opt; @@ -18,7 +19,7 @@ pub(crate) struct OutFile<'a> { pub struct Content { bytes: String, section_pending: bool, - blocks_pending: Vec, + blocks_pending: Vec, } impl<'a> OutFile<'a> { @@ -38,11 +39,11 @@ impl<'a> OutFile<'a> { self.content.get_mut().next_section(); } - pub fn begin_block(&mut self, block: &str) { + pub fn begin_block(&mut self, block: Block) { self.content.get_mut().begin_block(block); } - pub fn end_block(&mut self, block: &str) { + pub fn end_block(&mut self, block: Block) { self.content.get_mut().end_block(block); } @@ -95,15 +96,13 @@ impl Content { self.section_pending = true; } - pub fn begin_block(&mut self, block: &str) { - self.blocks_pending.push(block.to_owned()); + pub fn begin_block(&mut self, block: Block) { + self.blocks_pending.push(block); } - pub fn end_block(&mut self, block: &str) { + pub fn end_block(&mut self, block: Block) { if self.blocks_pending.pop().is_none() { - self.bytes.push_str("} // "); - self.bytes.push_str(block); - self.bytes.push('\n'); + Block::write_end(&block, &mut self.bytes); self.section_pending = true; } } @@ -119,8 +118,7 @@ impl Content { self.bytes.push('\n'); } for block in self.blocks_pending.drain(..) { - self.bytes.push_str(&block); - self.bytes.push_str(" {\n"); + Block::write_begin(&block, &mut self.bytes); } self.section_pending = false; } else if self.section_pending { diff --git a/gen/src/write.rs b/gen/src/write.rs index 57c699c..a1f168f 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,4 +1,5 @@ use crate::gen::alphasort::NamespaceEntries; +use crate::gen::block::Block; use crate::gen::out::OutFile; use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; @@ -107,7 +108,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } if !out.header { - out.begin_block("extern \"C\""); + out.begin_block(Block::ExternC); write_exception_glue(out, apis); for api in apis { match api { @@ -116,7 +117,7 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { _ => {} } } - out.end_block("extern \"C\""); + out.end_block(Block::ExternC); } for api in apis { @@ -127,10 +128,9 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } for (namespace, nested_ns_entries) in ns_entries.nested_content() { - let block = format!("namespace {}", namespace); - out.begin_block(&block); + out.begin_block(Block::UserDefinedNamespace(namespace.clone())); gen_namespace_contents(out, nested_ns_entries); - out.end_block(&block); + out.end_block(Block::UserDefinedNamespace(namespace.clone())); } } @@ -973,7 +973,7 @@ fn to_mangled(ty: &Type, types: &Types) -> Symbol { } fn write_generic_instantiations(out: &mut OutFile) { - out.begin_block("extern \"C\""); + out.begin_block(Block::ExternC); for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1009,10 +1009,10 @@ fn write_generic_instantiations(out: &mut OutFile) { } } } - out.end_block("extern \"C\""); + out.end_block(Block::ExternC); - out.begin_block("namespace rust"); - out.begin_block("inline namespace cxxbridge05"); + out.begin_block(Block::Namespace("rust")); + out.begin_block(Block::InlineNamespace("cxxbridge05")); for ty in out.types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { @@ -1026,8 +1026,8 @@ fn write_generic_instantiations(out: &mut OutFile) { } } } - out.end_block("namespace cxxbridge05"); - out.end_block("namespace rust"); + out.end_block(Block::InlineNamespace("cxxbridge05")); + out.end_block(Block::Namespace("rust")); } fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { From 53e593f309cb35d49843695c72f1cb55980f35d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:09:16 +0000 Subject: [PATCH 1190/2232] Merge pull request #400 from dtolnay/blockcopy Borrow syntax tree idents to make Block copyable --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index ea911f8..95b49a6 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -18,7 +18,7 @@ impl<'a> NamespaceEntries<'a> { &self.direct } - pub fn nested_content(&self) -> impl Iterator { + pub fn nested_content(&self) -> impl Iterator)> { self.nested.iter().map(|(k, entries)| (*k, entries)) } } diff --git a/gen/src/block.rs b/gen/src/block.rs index e7f7c63..7775c52 100644 --- a/gen/src/block.rs +++ b/gen/src/block.rs @@ -1,14 +1,15 @@ use proc_macro2::Ident; -pub enum Block { +#[derive(Copy, Clone)] +pub enum Block<'a> { AnonymousNamespace, Namespace(&'static str), - UserDefinedNamespace(Ident), + UserDefinedNamespace(&'a Ident), InlineNamespace(&'static str), ExternC, } -impl Block { +impl<'a> Block<'a> { pub fn write_begin(&self, out: &mut String) { if let Block::InlineNamespace(_) = self { out.push_str("inline "); diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index e755e10..4b8dce5 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -3,7 +3,7 @@ use crate::gen::ifndef; use crate::gen::out::{Content, OutFile}; #[derive(Default, PartialEq)] -pub struct Builtins { +pub struct Builtins<'a> { pub panic: bool, pub rust_string: bool, pub rust_str: bool, @@ -22,10 +22,10 @@ pub struct Builtins { pub rust_str_repr: bool, pub rust_slice_new: bool, pub rust_slice_repr: bool, - pub content: Content, + pub content: Content<'a>, } -impl Builtins { +impl<'a> Builtins<'a> { pub fn new() -> Self { Builtins::default() } diff --git a/gen/src/include.rs b/gen/src/include.rs index 8dc7146..d88c12d 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -19,7 +19,7 @@ pub struct Include { } #[derive(Default, PartialEq)] -pub struct Includes { +pub struct Includes<'a> { pub custom: Vec, pub array: bool, pub cstddef: bool, @@ -33,10 +33,10 @@ pub struct Includes { pub utility: bool, pub vector: bool, pub basetsd: bool, - pub content: Content, + pub content: Content<'a>, } -impl Includes { +impl<'a> Includes<'a> { pub fn new() -> Self { Includes::default() } @@ -101,13 +101,13 @@ pub(super) fn write(out: &mut OutFile) { } } -impl<'a> Extend<&'a Include> for Includes { - fn extend>(&mut self, iter: I) { +impl<'i, 'a> Extend<&'i Include> for Includes<'a> { + fn extend>(&mut self, iter: I) { self.custom.extend(iter.into_iter().cloned()); } } -impl<'a> From<&'a syntax::Include> for Include { +impl<'i> From<&'i syntax::Include> for Include { fn from(include: &syntax::Include) -> Self { Include { path: include.path.clone(), @@ -116,15 +116,15 @@ impl<'a> From<&'a syntax::Include> for Include { } } -impl Deref for Includes { - type Target = Content; +impl<'a> Deref for Includes<'a> { + type Target = Content<'a>; fn deref(&self) -> &Self::Target { &self.content } } -impl DerefMut for Includes { +impl<'a> DerefMut for Includes<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.content } diff --git a/gen/src/out.rs b/gen/src/out.rs index 606a586..6867a76 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -10,16 +10,16 @@ pub(crate) struct OutFile<'a> { pub header: bool, pub opt: &'a Opt, pub types: &'a Types<'a>, - pub include: Includes, - pub builtin: Builtins, - content: RefCell, + pub include: Includes<'a>, + pub builtin: Builtins<'a>, + content: RefCell>, } #[derive(Default)] -pub struct Content { +pub struct Content<'a> { bytes: String, section_pending: bool, - blocks_pending: Vec, + blocks_pending: Vec>, } impl<'a> OutFile<'a> { @@ -39,11 +39,11 @@ impl<'a> OutFile<'a> { self.content.get_mut().next_section(); } - pub fn begin_block(&mut self, block: Block) { + pub fn begin_block(&mut self, block: Block<'a>) { self.content.get_mut().begin_block(block); } - pub fn end_block(&mut self, block: Block) { + pub fn end_block(&mut self, block: Block<'a>) { self.content.get_mut().end_block(block); } @@ -74,20 +74,20 @@ impl<'a> OutFile<'a> { } } -impl Write for Content { +impl<'a> Write for Content<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { self.write(s); Ok(()) } } -impl PartialEq for Content { +impl<'a> PartialEq for Content<'a> { fn eq(&self, _other: &Content) -> bool { true } } -impl Content { +impl<'a> Content<'a> { fn new() -> Self { Content::default() } @@ -96,11 +96,11 @@ impl Content { self.section_pending = true; } - pub fn begin_block(&mut self, block: Block) { + pub fn begin_block(&mut self, block: Block<'a>) { self.blocks_pending.push(block); } - pub fn end_block(&mut self, block: Block) { + pub fn end_block(&mut self, block: Block<'a>) { if self.blocks_pending.pop().is_none() { Block::write_end(&block, &mut self.bytes); self.section_pending = true; diff --git a/gen/src/write.rs b/gen/src/write.rs index a1f168f..0b6a5aa 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -57,7 +57,7 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } -fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { +fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntries<'a>) { let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -128,9 +128,10 @@ fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries) { } for (namespace, nested_ns_entries) in ns_entries.nested_content() { - out.begin_block(Block::UserDefinedNamespace(namespace.clone())); + let block = Block::UserDefinedNamespace(namespace); + out.begin_block(block); gen_namespace_contents(out, nested_ns_entries); - out.end_block(Block::UserDefinedNamespace(namespace.clone())); + out.end_block(block); } } From f02146e4e2d0d3b6bd942e386d84bad2b75b720e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:09:25 +0000 Subject: [PATCH 1191/2232] Validate that block begins and ends are correctly paired --- diff --git a/gen/src/block.rs b/gen/src/block.rs index 19dc8b1..96a9a6e 100644 --- a/gen/src/block.rs +++ b/gen/src/block.rs @@ -1,6 +1,6 @@ use proc_macro2::Ident; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Block<'a> { AnonymousNamespace, Namespace(&'static str), diff --git a/gen/src/out.rs b/gen/src/out.rs index 7498c69..0f12e89 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -18,8 +18,9 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, + blocks: Vec>, section_pending: bool, - blocks_pending: Vec>, + blocks_pending: usize, } impl<'a> OutFile<'a> { @@ -97,11 +98,18 @@ impl<'a> Content<'a> { } pub fn begin_block(&mut self, block: Block<'a>) { - self.blocks_pending.push(block); + self.blocks.push(block); + self.blocks_pending += 1; } pub fn end_block(&mut self, block: Block<'a>) { - if self.blocks_pending.pop().is_none() { + let begin_block = self.blocks.pop().unwrap(); + let end_block = block; + assert_eq!(begin_block, end_block); + + if self.blocks_pending > 0 { + self.blocks_pending -= 1; + } else { Block::write_end(block, &mut self.bytes); self.section_pending = true; } @@ -113,21 +121,20 @@ impl<'a> Content<'a> { fn write(&mut self, b: &str) { if !b.is_empty() { - if !self.blocks_pending.is_empty() { + if self.blocks_pending > 0 { if !self.bytes.is_empty() { self.bytes.push('\n'); } - for block in self.blocks_pending.drain(..) { - Block::write_begin(block, &mut self.bytes); - } - self.section_pending = false; - } else if self.section_pending { - if !self.bytes.is_empty() { - self.bytes.push('\n'); + let pending = self.blocks.len() - self.blocks_pending..; + for block in &self.blocks[pending] { + Block::write_begin(*block, &mut self.bytes); } - self.section_pending = false; + } else if self.section_pending && !self.bytes.is_empty() { + self.bytes.push('\n'); } self.bytes.push_str(b); + self.section_pending = false; + self.blocks_pending = 0; } } } From 2a160e4effc070b48a0a789b94e545ff8054483e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:09:25 +0000 Subject: [PATCH 1192/2232] Pass Block only by Copy --- diff --git a/gen/src/block.rs b/gen/src/block.rs index 7775c52..19dc8b1 100644 --- a/gen/src/block.rs +++ b/gen/src/block.rs @@ -10,7 +10,7 @@ pub enum Block<'a> { } impl<'a> Block<'a> { - pub fn write_begin(&self, out: &mut String) { + pub fn write_begin(self, out: &mut String) { if let Block::InlineNamespace(_) = self { out.push_str("inline "); } @@ -18,13 +18,13 @@ impl<'a> Block<'a> { out.push_str(" {\n"); } - pub fn write_end(&self, out: &mut String) { + pub fn write_end(self, out: &mut String) { out.push_str("} // "); self.write_common(out); out.push('\n'); } - fn write_common(&self, out: &mut String) { + fn write_common(self, out: &mut String) { match self { Block::AnonymousNamespace => out.push_str("namespace"), Block::Namespace(name) => { diff --git a/gen/src/out.rs b/gen/src/out.rs index 6867a76..7498c69 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -102,7 +102,7 @@ impl<'a> Content<'a> { pub fn end_block(&mut self, block: Block<'a>) { if self.blocks_pending.pop().is_none() { - Block::write_end(&block, &mut self.bytes); + Block::write_end(block, &mut self.bytes); self.section_pending = true; } } @@ -118,7 +118,7 @@ impl<'a> Content<'a> { self.bytes.push('\n'); } for block in self.blocks_pending.drain(..) { - Block::write_begin(&block, &mut self.bytes); + Block::write_begin(block, &mut self.bytes); } self.section_pending = false; } else if self.section_pending { From e3b39820d12b89e926d9542216c3a7454164af91 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:09:25 +0000 Subject: [PATCH 1193/2232] Recognize identical block being closed and opened --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 0f12e89..2733c32 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -18,11 +18,17 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, - blocks: Vec>, + blocks: Vec>, section_pending: bool, blocks_pending: usize, } +#[derive(Copy, Clone, PartialEq, Debug)] +enum BlockBoundary<'a> { + Begin(Block<'a>), + End(Block<'a>), +} + impl<'a> OutFile<'a> { pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self { OutFile { @@ -53,10 +59,11 @@ impl<'a> OutFile<'a> { Write::write_fmt(content, args).unwrap(); } - pub fn content(&self) -> Vec { + pub fn content(&mut self) -> Vec { + self.flush(); let include = &self.include.content.bytes; let builtin = &self.builtin.content.bytes; - let content = &self.content.borrow().bytes; + let content = &self.content.get_mut().bytes; let len = include.len() + builtin.len() + content.len() + 2; let mut out = String::with_capacity(len); out.push_str(include); @@ -73,6 +80,12 @@ impl<'a> OutFile<'a> { } out.into_bytes() } + + fn flush(&mut self) { + self.include.content.flush(); + self.builtin.content.flush(); + self.content.get_mut().flush(); + } } impl<'a> Write for Content<'a> { @@ -98,21 +111,11 @@ impl<'a> Content<'a> { } pub fn begin_block(&mut self, block: Block<'a>) { - self.blocks.push(block); - self.blocks_pending += 1; + self.push_block_boundary(BlockBoundary::Begin(block)); } pub fn end_block(&mut self, block: Block<'a>) { - let begin_block = self.blocks.pop().unwrap(); - let end_block = block; - assert_eq!(begin_block, end_block); - - if self.blocks_pending > 0 { - self.blocks_pending -= 1; - } else { - Block::write_end(block, &mut self.bytes); - self.section_pending = true; - } + self.push_block_boundary(BlockBoundary::End(block)); } pub fn write_fmt(&mut self, args: Arguments) { @@ -122,14 +125,9 @@ impl<'a> Content<'a> { fn write(&mut self, b: &str) { if !b.is_empty() { if self.blocks_pending > 0 { - if !self.bytes.is_empty() { - self.bytes.push('\n'); - } - let pending = self.blocks.len() - self.blocks_pending..; - for block in &self.blocks[pending] { - Block::write_begin(*block, &mut self.bytes); - } - } else if self.section_pending && !self.bytes.is_empty() { + self.flush_blocks(); + } + if self.section_pending && !self.bytes.is_empty() { self.bytes.push('\n'); } self.bytes.push_str(b); @@ -137,4 +135,59 @@ impl<'a> Content<'a> { self.blocks_pending = 0; } } + + fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) { + if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() { + self.blocks.pop(); + self.blocks_pending -= 1; + } else { + self.blocks.push(boundary); + self.blocks_pending += 1; + } + } + + fn flush(&mut self) { + if self.blocks_pending > 0 { + self.flush_blocks(); + } + } + + fn flush_blocks(&mut self) { + self.section_pending = !self.bytes.is_empty(); + let mut read = self.blocks.len() - self.blocks_pending; + let mut write = read; + + while read < self.blocks.len() { + match self.blocks[read] { + BlockBoundary::Begin(begin_block) => { + if self.section_pending { + self.bytes.push('\n'); + self.section_pending = false; + } + Block::write_begin(begin_block, &mut self.bytes); + self.blocks[write] = BlockBoundary::Begin(begin_block); + write += 1; + } + BlockBoundary::End(end_block) => { + write = write.checked_sub(1).unwrap(); + let begin_block = self.blocks[write]; + assert_eq!(begin_block, BlockBoundary::Begin(end_block)); + Block::write_end(end_block, &mut self.bytes); + self.section_pending = true; + } + } + read += 1; + } + + self.blocks.truncate(write); + } +} + +impl<'a> BlockBoundary<'a> { + fn rev(self) -> BlockBoundary<'a> { + match self { + BlockBoundary::Begin(block) => BlockBoundary::End(block), + BlockBoundary::End(block) => BlockBoundary::Begin(block), + } + } } From 078c90fab6cdcecb981b7577a93d480883e2d7ed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:09:25 +0000 Subject: [PATCH 1194/2232] Implement set_namespace helper for OutFile --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 95b49a6..5c21bc4 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -1,9 +1,11 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { + pub namespace: Namespace, direct: Vec<&'a Api>, nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } @@ -45,7 +47,17 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) .collect(); - NamespaceEntries { direct, nested } + let namespace = apis + .first() + .copied() + .and_then(Api::namespace) + .map_or(Namespace::ROOT, |ns| ns.iter().take(depth).collect()); + + NamespaceEntries { + namespace, + direct, + nested, + } } #[cfg(test)] diff --git a/gen/src/out.rs b/gen/src/out.rs index 2733c32..bf880cc 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -2,6 +2,7 @@ use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::Opt; +use crate::syntax::namespace::Namespace; use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; @@ -18,6 +19,7 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, + namespace: &'a Namespace, blocks: Vec>, section_pending: bool, blocks_pending: usize, @@ -54,6 +56,10 @@ impl<'a> OutFile<'a> { self.content.get_mut().end_block(block); } + pub fn set_namespace(&mut self, namespace: &'a Namespace) { + self.content.get_mut().set_namespace(namespace); + } + pub fn write_fmt(&self, args: Arguments) { let content = &mut *self.content.borrow_mut(); Write::write_fmt(content, args).unwrap(); @@ -118,6 +124,16 @@ impl<'a> Content<'a> { self.push_block_boundary(BlockBoundary::End(block)); } + pub fn set_namespace(&mut self, namespace: &'a Namespace) { + for name in self.namespace.iter().rev() { + self.end_block(Block::UserDefinedNamespace(name)); + } + for name in namespace { + self.begin_block(Block::UserDefinedNamespace(name)); + } + self.namespace = namespace; + } + pub fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } @@ -147,6 +163,7 @@ impl<'a> Content<'a> { } fn flush(&mut self) { + self.set_namespace(Default::default()); if self.blocks_pending > 0 { self.flush_blocks(); } diff --git a/gen/src/write.rs b/gen/src/write.rs index 0b6a5aa..352d643 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -57,7 +57,8 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } -fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntries<'a>) { +fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { + out.set_namespace(&ns_entries.namespace); let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -127,11 +128,8 @@ fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntri } } - for (namespace, nested_ns_entries) in ns_entries.nested_content() { - let block = Block::UserDefinedNamespace(namespace); - out.begin_block(block); + for (_, nested_ns_entries) in ns_entries.nested_content() { gen_namespace_contents(out, nested_ns_entries); - out.end_block(block); } } @@ -974,6 +972,7 @@ fn to_mangled(ty: &Type, types: &Types) -> Symbol { } fn write_generic_instantiations(out: &mut OutFile) { + out.set_namespace(Default::default()); out.begin_block(Block::ExternC); for ty in out.types { if let Type::RustBox(ty) = ty { diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 834a1d5..7628b0a 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -2,6 +2,7 @@ use crate::syntax::qualified::QualifiedName; use crate::syntax::Api; 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}; @@ -10,7 +11,7 @@ mod kw { syn::custom_keyword!(namespace); } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct Namespace { segments: Vec, } @@ -37,6 +38,13 @@ impl Namespace { } } +impl Default for &Namespace { + fn default() -> Self { + const ROOT: &Namespace = &Namespace::ROOT; + ROOT + } +} + impl Parse for Namespace { fn parse(input: ParseStream) -> Result { let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments; @@ -67,6 +75,16 @@ impl<'a> IntoIterator for &'a Namespace { } } +impl<'a> FromIterator<&'a Ident> for Namespace { + fn from_iter(idents: I) -> Self + where + I: IntoIterator, + { + let segments = idents.into_iter().cloned().collect(); + Namespace { segments } + } +} + impl Api { pub fn namespace(&self) -> Option<&Namespace> { match self { From 6b5d4dde00f645749ad699a7b8c36aa9e8644a17 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:16:29 +0000 Subject: [PATCH 1195/2232] Merge pull request #401 from dtolnay/block Validate that block begins and ends are correctly paired --- diff --git a/gen/src/block.rs b/gen/src/block.rs index 19dc8b1..96a9a6e 100644 --- a/gen/src/block.rs +++ b/gen/src/block.rs @@ -1,6 +1,6 @@ use proc_macro2::Ident; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Block<'a> { AnonymousNamespace, Namespace(&'static str), diff --git a/gen/src/out.rs b/gen/src/out.rs index 7498c69..0f12e89 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -18,8 +18,9 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, + blocks: Vec>, section_pending: bool, - blocks_pending: Vec>, + blocks_pending: usize, } impl<'a> OutFile<'a> { @@ -97,11 +98,18 @@ impl<'a> Content<'a> { } pub fn begin_block(&mut self, block: Block<'a>) { - self.blocks_pending.push(block); + self.blocks.push(block); + self.blocks_pending += 1; } pub fn end_block(&mut self, block: Block<'a>) { - if self.blocks_pending.pop().is_none() { + let begin_block = self.blocks.pop().unwrap(); + let end_block = block; + assert_eq!(begin_block, end_block); + + if self.blocks_pending > 0 { + self.blocks_pending -= 1; + } else { Block::write_end(block, &mut self.bytes); self.section_pending = true; } @@ -113,21 +121,20 @@ impl<'a> Content<'a> { fn write(&mut self, b: &str) { if !b.is_empty() { - if !self.blocks_pending.is_empty() { + if self.blocks_pending > 0 { if !self.bytes.is_empty() { self.bytes.push('\n'); } - for block in self.blocks_pending.drain(..) { - Block::write_begin(block, &mut self.bytes); - } - self.section_pending = false; - } else if self.section_pending { - if !self.bytes.is_empty() { - self.bytes.push('\n'); + let pending = self.blocks.len() - self.blocks_pending..; + for block in &self.blocks[pending] { + Block::write_begin(*block, &mut self.bytes); } - self.section_pending = false; + } else if self.section_pending && !self.bytes.is_empty() { + self.bytes.push('\n'); } self.bytes.push_str(b); + self.section_pending = false; + self.blocks_pending = 0; } } } From 118a6a90388f9c947bff03cbd38b99e31f6d716d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:23:04 +0000 Subject: [PATCH 1196/2232] Merge pull request #402 from dtolnay/closeopen Recognize identical block being closed and opened --- diff --git a/gen/src/out.rs b/gen/src/out.rs index 0f12e89..2733c32 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -18,11 +18,17 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, - blocks: Vec>, + blocks: Vec>, section_pending: bool, blocks_pending: usize, } +#[derive(Copy, Clone, PartialEq, Debug)] +enum BlockBoundary<'a> { + Begin(Block<'a>), + End(Block<'a>), +} + impl<'a> OutFile<'a> { pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self { OutFile { @@ -53,10 +59,11 @@ impl<'a> OutFile<'a> { Write::write_fmt(content, args).unwrap(); } - pub fn content(&self) -> Vec { + pub fn content(&mut self) -> Vec { + self.flush(); let include = &self.include.content.bytes; let builtin = &self.builtin.content.bytes; - let content = &self.content.borrow().bytes; + let content = &self.content.get_mut().bytes; let len = include.len() + builtin.len() + content.len() + 2; let mut out = String::with_capacity(len); out.push_str(include); @@ -73,6 +80,12 @@ impl<'a> OutFile<'a> { } out.into_bytes() } + + fn flush(&mut self) { + self.include.content.flush(); + self.builtin.content.flush(); + self.content.get_mut().flush(); + } } impl<'a> Write for Content<'a> { @@ -98,21 +111,11 @@ impl<'a> Content<'a> { } pub fn begin_block(&mut self, block: Block<'a>) { - self.blocks.push(block); - self.blocks_pending += 1; + self.push_block_boundary(BlockBoundary::Begin(block)); } pub fn end_block(&mut self, block: Block<'a>) { - let begin_block = self.blocks.pop().unwrap(); - let end_block = block; - assert_eq!(begin_block, end_block); - - if self.blocks_pending > 0 { - self.blocks_pending -= 1; - } else { - Block::write_end(block, &mut self.bytes); - self.section_pending = true; - } + self.push_block_boundary(BlockBoundary::End(block)); } pub fn write_fmt(&mut self, args: Arguments) { @@ -122,14 +125,9 @@ impl<'a> Content<'a> { fn write(&mut self, b: &str) { if !b.is_empty() { if self.blocks_pending > 0 { - if !self.bytes.is_empty() { - self.bytes.push('\n'); - } - let pending = self.blocks.len() - self.blocks_pending..; - for block in &self.blocks[pending] { - Block::write_begin(*block, &mut self.bytes); - } - } else if self.section_pending && !self.bytes.is_empty() { + self.flush_blocks(); + } + if self.section_pending && !self.bytes.is_empty() { self.bytes.push('\n'); } self.bytes.push_str(b); @@ -137,4 +135,59 @@ impl<'a> Content<'a> { self.blocks_pending = 0; } } + + fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) { + if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() { + self.blocks.pop(); + self.blocks_pending -= 1; + } else { + self.blocks.push(boundary); + self.blocks_pending += 1; + } + } + + fn flush(&mut self) { + if self.blocks_pending > 0 { + self.flush_blocks(); + } + } + + fn flush_blocks(&mut self) { + self.section_pending = !self.bytes.is_empty(); + let mut read = self.blocks.len() - self.blocks_pending; + let mut write = read; + + while read < self.blocks.len() { + match self.blocks[read] { + BlockBoundary::Begin(begin_block) => { + if self.section_pending { + self.bytes.push('\n'); + self.section_pending = false; + } + Block::write_begin(begin_block, &mut self.bytes); + self.blocks[write] = BlockBoundary::Begin(begin_block); + write += 1; + } + BlockBoundary::End(end_block) => { + write = write.checked_sub(1).unwrap(); + let begin_block = self.blocks[write]; + assert_eq!(begin_block, BlockBoundary::Begin(end_block)); + Block::write_end(end_block, &mut self.bytes); + self.section_pending = true; + } + } + read += 1; + } + + self.blocks.truncate(write); + } +} + +impl<'a> BlockBoundary<'a> { + fn rev(self) -> BlockBoundary<'a> { + match self { + BlockBoundary::Begin(block) => BlockBoundary::End(block), + BlockBoundary::End(block) => BlockBoundary::Begin(block), + } + } } From 5a21a9838602516c60b421191386fa5c14de7107 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 02:27:22 +0000 Subject: [PATCH 1197/2232] Merge pull request #403 from dtolnay/setnamespace Implement set_namespace helper for OutFile --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 95b49a6..5c21bc4 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -1,9 +1,11 @@ +use crate::syntax::namespace::Namespace; use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { + pub namespace: Namespace, direct: Vec<&'a Api>, nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } @@ -45,7 +47,17 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) .collect(); - NamespaceEntries { direct, nested } + let namespace = apis + .first() + .copied() + .and_then(Api::namespace) + .map_or(Namespace::ROOT, |ns| ns.iter().take(depth).collect()); + + NamespaceEntries { + namespace, + direct, + nested, + } } #[cfg(test)] diff --git a/gen/src/out.rs b/gen/src/out.rs index 2733c32..bf880cc 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -2,6 +2,7 @@ use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::Opt; +use crate::syntax::namespace::Namespace; use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; @@ -18,6 +19,7 @@ pub(crate) struct OutFile<'a> { #[derive(Default)] pub struct Content<'a> { bytes: String, + namespace: &'a Namespace, blocks: Vec>, section_pending: bool, blocks_pending: usize, @@ -54,6 +56,10 @@ impl<'a> OutFile<'a> { self.content.get_mut().end_block(block); } + pub fn set_namespace(&mut self, namespace: &'a Namespace) { + self.content.get_mut().set_namespace(namespace); + } + pub fn write_fmt(&self, args: Arguments) { let content = &mut *self.content.borrow_mut(); Write::write_fmt(content, args).unwrap(); @@ -118,6 +124,16 @@ impl<'a> Content<'a> { self.push_block_boundary(BlockBoundary::End(block)); } + pub fn set_namespace(&mut self, namespace: &'a Namespace) { + for name in self.namespace.iter().rev() { + self.end_block(Block::UserDefinedNamespace(name)); + } + for name in namespace { + self.begin_block(Block::UserDefinedNamespace(name)); + } + self.namespace = namespace; + } + pub fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } @@ -147,6 +163,7 @@ impl<'a> Content<'a> { } fn flush(&mut self) { + self.set_namespace(Default::default()); if self.blocks_pending > 0 { self.flush_blocks(); } diff --git a/gen/src/write.rs b/gen/src/write.rs index 0b6a5aa..352d643 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -57,7 +57,8 @@ fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceE } } -fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntries<'a>) { +fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { + out.set_namespace(&ns_entries.namespace); let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -127,11 +128,8 @@ fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &NamespaceEntri } } - for (namespace, nested_ns_entries) in ns_entries.nested_content() { - let block = Block::UserDefinedNamespace(namespace); - out.begin_block(block); + for (_, nested_ns_entries) in ns_entries.nested_content() { gen_namespace_contents(out, nested_ns_entries); - out.end_block(block); } } @@ -974,6 +972,7 @@ fn to_mangled(ty: &Type, types: &Types) -> Symbol { } fn write_generic_instantiations(out: &mut OutFile) { + out.set_namespace(Default::default()); out.begin_block(Block::ExternC); for ty in out.types { if let Type::RustBox(ty) = ty { diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 834a1d5..7628b0a 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -2,6 +2,7 @@ use crate::syntax::qualified::QualifiedName; use crate::syntax::Api; 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}; @@ -10,7 +11,7 @@ mod kw { syn::custom_keyword!(namespace); } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct Namespace { segments: Vec, } @@ -37,6 +38,13 @@ impl Namespace { } } +impl Default for &Namespace { + fn default() -> Self { + const ROOT: &Namespace = &Namespace::ROOT; + ROOT + } +} + impl Parse for Namespace { fn parse(input: ParseStream) -> Result { let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments; @@ -67,6 +75,16 @@ impl<'a> IntoIterator for &'a Namespace { } } +impl<'a> FromIterator<&'a Ident> for Namespace { + fn from_iter(idents: I) -> Self + where + I: IntoIterator, + { + let segments = idents.into_iter().cloned().collect(); + Namespace { segments } + } +} + impl Api { pub fn namespace(&self) -> Option<&Namespace> { match self { From e279735b790d95b78fd245f983dee26df713b1d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 03:08:37 +0000 Subject: [PATCH 1198/2232] Standardize on write_ prefix for functions that write to a &mut OutFile --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 352d643..a2d41a7 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -23,8 +23,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec Vec(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { +fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { out.set_namespace(&ns_entries.namespace); let apis = ns_entries.direct_content(); @@ -129,7 +129,7 @@ fn gen_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEn } for (_, nested_ns_entries) in ns_entries.nested_content() { - gen_namespace_contents(out, nested_ns_entries); + write_namespace_contents(out, nested_ns_entries); } } From 58711a9563e4abf71e0c0456246a5493cf640df8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 03:11:37 +0000 Subject: [PATCH 1199/2232] Move #pragma once to where header block is written --- diff --git a/gen/src/include.rs b/gen/src/include.rs index d88c12d..cf718da 100644 --- a/gen/src/include.rs +++ b/gen/src/include.rs @@ -47,9 +47,14 @@ impl<'a> Includes<'a> { } pub(super) fn write(out: &mut OutFile) { + let header = out.header; let include = &mut out.include; let out = &mut include.content; + if header { + writeln!(out, "#pragma once"); + } + for include in &include.custom { match include.kind { IncludeKind::Quoted => { diff --git a/gen/src/write.rs b/gen/src/write.rs index a2d41a7..08bb1fc 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -15,10 +15,6 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec Date: Nov 02 2020 03:44:58 +0000 Subject: [PATCH 1200/2232] Perform exhaustive match in pick_includes_and_builtins --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 08bb1fc..2b4e552 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -179,7 +179,7 @@ fn pick_includes_and_builtins(out: &mut OutFile) { out.include.cstdint = true; out.builtin.rust_slice = true; } - _ => {} + Type::Ref(_) | Type::Void(_) => {} } } } From 1f010c6ef07c270b9045f51d5901dc814ec0bfe0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:27:57 +0000 Subject: [PATCH 1201/2232] Move cxxbridge05$exception declaration to builtins --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 4b8dce5..36e32f8 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -22,6 +22,7 @@ pub struct Builtins<'a> { pub rust_str_repr: bool, pub rust_slice_new: bool, pub rust_slice_repr: bool, + pub exception: bool, pub content: Content<'a>, } @@ -195,4 +196,13 @@ pub(super) fn write(out: &mut OutFile) { } out.end_block(Block::Namespace("rust")); + + if builtin.exception { + out.begin_block(Block::ExternC); + writeln!( + out, + "const char *cxxbridge05$exception(const char *, size_t);", + ); + out.end_block(Block::ExternC); + } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 2b4e552..3498ef9 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -106,7 +106,6 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace if !out.header { out.begin_block(Block::ExternC); - write_exception_glue(out, apis); for api in apis { match api { Api::CxxFunction(efn) => write_cxx_function_shim(out, efn), @@ -316,26 +315,6 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { ); } -fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { - let mut has_cxx_throws = false; - for api in apis { - if let Api::CxxFunction(efn) = api { - if efn.throws { - has_cxx_throws = true; - break; - } - } - } - - if has_cxx_throws { - out.next_section(); - writeln!( - out, - "const char *cxxbridge05$exception(const char *, size_t);", - ); - } -} - fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { out.next_section(); if let Some(annotation) = &out.opt.cxx_impl_annotations { @@ -500,13 +479,14 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { writeln!(out, ";"); if efn.throws { out.include.cstring = true; + out.builtin.exception = true; writeln!(out, " throw$.ptr = nullptr;"); writeln!(out, " }},"); writeln!(out, " [&](const char *catch$) noexcept {{"); writeln!(out, " throw$.len = ::std::strlen(catch$);"); writeln!( out, - " throw$.ptr = cxxbridge05$exception(catch$, throw$.len);", + " throw$.ptr = ::cxxbridge05$exception(catch$, throw$.len);", ); writeln!(out, " }});"); writeln!(out, " return throw$;"); From ca563ee3359fa13cdf305a181d6a4c1c0d025880 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:29:35 +0000 Subject: [PATCH 1202/2232] Move Block::ExternC to the individual items --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 3498ef9..dfa0502 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -105,7 +105,6 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace } if !out.header { - out.begin_block(Block::ExternC); for api in apis { match api { Api::CxxFunction(efn) => write_cxx_function_shim(out, efn), @@ -113,7 +112,6 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace _ => {} } } - out.end_block(Block::ExternC); } for api in apis { @@ -317,6 +315,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { out.next_section(); + out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); } @@ -498,6 +497,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { write_function_pointer_trampoline(out, efn, var, f); } } + out.end_block(Block::ExternC); } fn write_function_pointer_trampoline( @@ -516,9 +516,11 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn) { + out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, indirect_call); + out.end_block(Block::ExternC); } fn write_rust_function_decl_impl( From 0b9b9f84b6ee591717ca073e5cbd38440465387d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:41:30 +0000 Subject: [PATCH 1203/2232] Specify lifetimes which are connected to OutFile --- diff --git a/gen/src/write.rs b/gen/src/write.rs index dfa0502..abc965a 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -181,7 +181,7 @@ fn pick_includes_and_builtins(out: &mut OutFile) { } } -fn write_struct(out: &mut OutFile, strct: &Struct) { +fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -211,7 +211,11 @@ fn write_struct_using(out: &mut OutFile, ident: &CppName) { ); } -fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { +fn write_struct_with_methods<'a>( + out: &mut OutFile<'a>, + ety: &'a ExternType, + methods: &[&ExternFn], +) { let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -236,7 +240,7 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex writeln!(out, "#endif // {}", guard); } -fn write_enum(out: &mut OutFile, enm: &Enum) { +fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -253,7 +257,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { writeln!(out, "#endif // {}", guard); } -fn check_enum(out: &mut OutFile, enm: &Enum) { +fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { write!( out, "static_assert(sizeof({}) == sizeof(", @@ -313,7 +317,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { ); } -fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn) { +fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.next_section(); out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { @@ -515,7 +519,7 @@ fn write_function_pointer_trampoline( write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call); } -fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn) { +fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -573,7 +577,7 @@ fn write_rust_function_decl_impl( writeln!(out, ") noexcept;"); } -fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn) { +fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } From d7a3a18e573153203381253377fac68ba985f57b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:45:38 +0000 Subject: [PATCH 1204/2232] Prefer namespace over ns --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 5c21bc4..08ce4b4 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -29,8 +29,8 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut direct = Vec::new(); let mut nested_namespaces = BTreeMap::new(); for api in &apis { - if let Some(ns) = api.namespace() { - let first_ns_elem = ns.iter().nth(depth); + if let Some(namespace) = api.namespace() { + let first_ns_elem = namespace.iter().nth(depth); if let Some(first_ns_elem) = first_ns_elem { nested_namespaces .entry(first_ns_elem) diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 3796431..e55a08a 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -43,7 +43,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) { - Ok(ns) => ns, + Ok(namespace) => namespace, Err(err) => return err.to_compile_error().into(), }; let mut ffi = parse_macro_input!(input as Module); diff --git a/syntax/ident.rs b/syntax/ident.rs index 354790a..03c4380 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -13,7 +13,7 @@ fn check(cx: &mut Check, ident: &Ident) { } fn check_ident(cx: &mut Check, ident: &CppName) { - for segment in &ident.ns { + for segment in &ident.namespace { check(cx, segment); } check(cx, &ident.ident); diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 9255feb..d1f3adc 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -15,13 +15,13 @@ pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { Some(receiver) => { let receiver_ident = types.resolve(&receiver.ty); join!( - efn.ident.cxx.ns, + efn.ident.cxx.namespace, CXXBRIDGE, receiver_ident.ident, efn.ident.rust ) } - None => join!(efn.ident.cxx.ns, CXXBRIDGE, efn.ident.rust), + None => join!(efn.ident.cxx.namespace, CXXBRIDGE, efn.ident.rust), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 90af17f..b12d72b 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -197,7 +197,7 @@ pub struct Pair { // to output it as a qualified name or as an unqualfied name. #[derive(Clone)] pub struct CppName { - pub ns: Namespace, + pub namespace: Namespace, pub ident: Ident, } diff --git a/syntax/names.rs b/syntax/names.rs index a61ffc3..ee010a9 100644 --- a/syntax/names.rs +++ b/syntax/names.rs @@ -5,19 +5,23 @@ use syn::Token; impl Pair { /// Use this constructor when the item can't have a different /// name in Rust and C++. - pub fn new(ns: Namespace, ident: Ident) -> Self { + pub fn new(namespace: Namespace, ident: Ident) -> Self { Self { rust: ident.clone(), - cxx: CppName::new(ns, ident), + cxx: CppName::new(namespace, ident), } } /// Use this constructor when attributes such as #[rust_name] /// can be used to potentially give a different name in Rust vs C++. - pub fn new_from_differing_names(ns: Namespace, cxx_ident: Ident, rust_ident: Ident) -> Self { + pub fn new_from_differing_names( + namespace: Namespace, + cxx_ident: Ident, + rust_ident: Ident, + ) -> Self { Self { rust: rust_ident, - cxx: CppName::new(ns, cxx_ident), + cxx: CppName::new(namespace, cxx_ident), } } } @@ -47,12 +51,12 @@ impl ResolvableName { } impl CppName { - pub fn new(ns: Namespace, ident: Ident) -> Self { - Self { ns, ident } + pub fn new(namespace: Namespace, ident: Ident) -> Self { + Self { namespace, ident } } fn iter_all_segments(&self) -> impl Iterator { - self.ns.iter().chain(std::iter::once(&self.ident)) + self.namespace.iter().chain(std::iter::once(&self.ident)) } fn join(&self, sep: &str) -> String { diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 7628b0a..02e5dc2 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -32,9 +32,9 @@ impl Namespace { input.parse::()?; input.parse::()?; - let ns = input.parse::()?; + let namespace = input.parse::()?; input.parse::>()?; - Ok(ns) + Ok(namespace) } } @@ -88,10 +88,10 @@ impl<'a> FromIterator<&'a Ident> for Namespace { impl Api { pub fn namespace(&self) -> Option<&Namespace> { match self { - Api::CxxFunction(efn) | Api::RustFunction(efn) => Some(&efn.ident.cxx.ns), - Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident.cxx.ns), - Api::Enum(enm) => Some(&enm.ident.cxx.ns), - Api::Struct(strct) => Some(&strct.ident.cxx.ns), + Api::CxxFunction(efn) | Api::RustFunction(efn) => Some(&efn.ident.cxx.namespace), + Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident.cxx.namespace), + Api::Enum(enm) => Some(&enm.ident.cxx.namespace), + Api::Struct(strct) => Some(&strct.ident.cxx.namespace), Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, } } diff --git a/syntax/parse.rs b/syntax/parse.rs index 1927554..c170f79 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -21,22 +21,27 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namespace) -> Vec { +pub fn parse_items( + cx: &mut Errors, + items: Vec, + trusted: bool, + namespace: &Namespace, +) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(cx, item, ns.clone()) { + Item::Struct(item) => match parse_struct(cx, item, namespace.clone()) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(cx, item, ns.clone()) { + Item::Enum(item) => match parse_enum(cx, item, namespace.clone()) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, Item::ForeignMod(foreign_mod) => { - parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, ns) + parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace) } - Item::Impl(item) => match parse_impl(item, ns) { + Item::Impl(item) => match parse_impl(item, namespace) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, @@ -47,7 +52,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namesp apis } -fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct, mut namespace: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -68,7 +73,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result< attrs::Parser { doc: Some(&mut doc), derives: Some(&mut derives), - namespace: Some(&mut ns), + namespace: Some(&mut namespace), ..Default::default() }, ); @@ -85,7 +90,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result< doc, derives, struct_token: item.struct_token, - ident: Pair::new(ns.clone(), item.ident), + ident: Pair::new(namespace.clone(), item.ident), brace_token: fields.brace_token, fields: fields .named @@ -93,14 +98,14 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct, mut ns: Namespace) -> Result< .map(|field| { Ok(Var { ident: field.ident.unwrap(), - ty: parse_type(&field.ty, &ns)?, + ty: parse_type(&field.ty, &namespace)?, }) }) .collect::>()?, })) } -fn parse_enum(cx: &mut Errors, item: ItemEnum, mut ns: Namespace) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum, mut namespace: Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -121,7 +126,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, mut ns: Namespace) -> Result attrs::Parser { doc: Some(&mut doc), repr: Some(&mut repr), - namespace: Some(&mut ns), + namespace: Some(&mut namespace), ..Default::default() }, ); @@ -172,7 +177,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, mut ns: Namespace) -> Result Ok(Api::Enum(Enum { doc, enum_token, - ident: Pair::new(ns, item.ident), + ident: Pair::new(namespace, item.ident), brace_token, variants, repr, @@ -184,7 +189,7 @@ fn parse_foreign_mod( foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, - ns: &Namespace, + namespace: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, @@ -209,12 +214,13 @@ fn parse_foreign_mod( for foreign in &foreign_mod.items { match foreign { ForeignItem::Type(foreign) => { - match parse_extern_type(cx, foreign, lang, trusted, ns.clone()) { + match parse_extern_type(cx, foreign, lang, trusted, namespace.clone()) { Ok(ety) => items.push(ety), Err(err) => cx.push(err), } } - ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, ns.clone()) { + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, namespace.clone()) + { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -225,7 +231,7 @@ fn parse_foreign_mod( } } ForeignItem::Verbatim(tokens) => { - match parse_extern_verbatim(cx, tokens, lang, ns.clone()) { + match parse_extern_verbatim(cx, tokens, lang, namespace.clone()) { Ok(api) => items.push(api), Err(err) => cx.push(err), } @@ -277,7 +283,7 @@ fn parse_extern_type( foreign_type: &ForeignItemType, lang: Lang, trusted: bool, - mut ns: Namespace, + mut namespace: Namespace, ) -> Result { let mut doc = Doc::new(); attrs::parse( @@ -285,7 +291,7 @@ fn parse_extern_type( &foreign_type.attrs, attrs::Parser { doc: Some(&mut doc), - namespace: Some(&mut ns), + namespace: Some(&mut namespace), ..Default::default() }, ); @@ -299,7 +305,7 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident: Pair::new(ns, ident), + ident: Pair::new(namespace, ident), semi_token, trusted, })) @@ -309,7 +315,7 @@ fn parse_extern_fn( cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang, - mut ns: Namespace, + mut namespace: Namespace, ) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { @@ -335,7 +341,7 @@ fn parse_extern_fn( doc: Some(&mut doc), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), - namespace: Some(&mut ns), + namespace: Some(&mut namespace), ..Default::default() }, ); @@ -367,7 +373,7 @@ fn parse_extern_fn( } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; - let ty = parse_type(&arg.ty, &ns)?; + let ty = parse_type(&arg.ty, &namespace)?; if ident != "self" { args.push_value(Var { ident, ty }); if let Some(comma) = comma { @@ -394,12 +400,12 @@ fn parse_extern_fn( } let mut throws_tokens = None; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &ns)?; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &namespace)?; let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = Pair::new_from_differing_names( - ns, + namespace, cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), rust_name.unwrap_or(foreign_fn.sig.ident.clone()), ); @@ -432,7 +438,7 @@ fn parse_extern_verbatim( cx: &mut Errors, tokens: &TokenStream, lang: Lang, - mut ns: Namespace, + mut namespace: Namespace, ) -> Result { // type Alias = crate::path::to::Type; let parse = |input: ParseStream| -> Result { @@ -454,7 +460,7 @@ fn parse_extern_verbatim( &attrs, attrs::Parser { doc: Some(&mut doc), - namespace: Some(&mut ns), + namespace: Some(&mut namespace), ..Default::default() }, ); @@ -462,7 +468,7 @@ fn parse_extern_verbatim( Ok(TypeAlias { doc, type_token, - ident: Pair::new(ns, ident), + ident: Pair::new(namespace, ident), eq_token, ty, semi_token, @@ -481,7 +487,7 @@ fn parse_extern_verbatim( } } -fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { +fn parse_impl(imp: ItemImpl, namespace: &Namespace) -> Result { if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span); @@ -507,7 +513,7 @@ fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { Ok(Api::Impl(Impl { impl_token: imp.impl_token, - ty: parse_type(&self_ty, ns)?, + ty: parse_type(&self_ty, namespace)?, brace_token: imp.brace_token, })) } @@ -556,19 +562,19 @@ fn parse_include(input: ParseStream) -> Result { Err(input.error("expected \"quoted/path/to\" or ")) } -fn parse_type(ty: &RustType, ns: &Namespace) -> Result { +fn parse_type(ty: &RustType, namespace: &Namespace) -> Result { match ty { - RustType::Reference(ty) => parse_type_reference(ty, ns), - RustType::Path(ty) => parse_type_path(ty, ns), - RustType::Slice(ty) => parse_type_slice(ty, ns), - RustType::BareFn(ty) => parse_type_fn(ty, ns), + RustType::Reference(ty) => parse_type_reference(ty, namespace), + RustType::Path(ty) => parse_type_path(ty, namespace), + RustType::Slice(ty) => parse_type_slice(ty, namespace), + RustType::BareFn(ty) => parse_type_fn(ty, namespace), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { - let inner = parse_type(&ty.elem, ns)?; +fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result { + let inner = parse_type(&ty.elem, namespace)?; let which = match &inner { Type::Ident(ident) if ident.rust == "str" => { if ty.mutability.is_some() { @@ -591,7 +597,7 @@ fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { }))) } -fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { +fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; @@ -602,7 +608,7 @@ fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg, ns)?; + let inner = parse_type(arg, namespace)?; return Ok(Type::UniquePtr(Box::new(Ty1 { name: maybe_resolved_ident, langle: generic.lt_token, @@ -612,7 +618,7 @@ fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg, ns)?; + let inner = parse_type(arg, namespace)?; return Ok(Type::CxxVector(Box::new(Ty1 { name: maybe_resolved_ident, langle: generic.lt_token, @@ -622,7 +628,7 @@ fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg, ns)?; + let inner = parse_type(arg, namespace)?; return Ok(Type::RustBox(Box::new(Ty1 { name: maybe_resolved_ident, langle: generic.lt_token, @@ -632,7 +638,7 @@ fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg, ns)?; + let inner = parse_type(arg, namespace)?; return Ok(Type::RustVec(Box::new(Ty1 { name: maybe_resolved_ident, langle: generic.lt_token, @@ -648,15 +654,15 @@ fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { Err(Error::new_spanned(ty, "unsupported type")) } -fn parse_type_slice(ty: &TypeSlice, ns: &Namespace) -> Result { - let inner = parse_type(&ty.elem, ns)?; +fn parse_type_slice(ty: &TypeSlice, namespace: &Namespace) -> Result { + let inner = parse_type(&ty.elem, namespace)?; Ok(Type::Slice(Box::new(Slice { bracket: ty.bracket_token, inner, }))) } -fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { +fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, @@ -674,7 +680,7 @@ fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty, ns)?; + let ty = parse_type(&arg.ty, namespace)?; let ident = match &arg.name { Some(ident) => ident.0.clone(), None => format_ident!("_{}", i), @@ -683,7 +689,7 @@ fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { }) .collect::>()?; let mut throws_tokens = None; - let ret = parse_return_type(&ty.output, &mut throws_tokens, ns)?; + let ret = parse_return_type(&ty.output, &mut throws_tokens, namespace)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { unsafety: ty.unsafety, @@ -700,7 +706,7 @@ fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, - ns: &Namespace, + namespace: &Namespace, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), @@ -722,7 +728,7 @@ fn parse_return_type( } } } - match parse_type(ret, ns)? { + match parse_type(ret, namespace)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 0b79d5f..c849a62 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -81,7 +81,7 @@ impl Segment for Namespace { impl Segment for CppName { fn write(&self, symbol: &mut Symbol) { - self.ns.write(symbol); + self.namespace.write(symbol); self.ident.write(symbol); } } From b63ed5aa2c7a7b23a731f46c837ce9f49e8745f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:46:33 +0000 Subject: [PATCH 1205/2232] Move set_namespace to individual items --- diff --git a/gen/src/write.rs b/gen/src/write.rs index abc965a..0369ef5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -54,7 +54,6 @@ fn write_namespace_forward_declarations(out: &mut OutFile, ns_entries: &Namespac } fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { - out.set_namespace(&ns_entries.namespace); let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -182,6 +181,7 @@ fn pick_includes_and_builtins(out: &mut OutFile) { } fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { + out.set_namespace(&strct.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -216,6 +216,7 @@ fn write_struct_with_methods<'a>( ety: &'a ExternType, methods: &[&ExternFn], ) { + out.set_namespace(&ety.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -241,6 +242,7 @@ fn write_struct_with_methods<'a>( } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + out.set_namespace(&enm.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -258,6 +260,7 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + out.set_namespace(&enm.ident.cxx.namespace); write!( out, "static_assert(sizeof({}) == sizeof(", @@ -319,6 +322,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.next_section(); + out.set_namespace(&efn.ident.cxx.namespace); out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); @@ -520,6 +524,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { + out.set_namespace(&efn.ident.cxx.namespace); out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -578,6 +583,7 @@ fn write_rust_function_decl_impl( } fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { + out.set_namespace(&efn.ident.cxx.namespace); for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } From 3ff4fe48a57c9c44dd7070579093a132685ba22c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:46:49 +0000 Subject: [PATCH 1206/2232] Remove namespace from NamespaceEntries --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 08ce4b4..7309aa9 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -1,11 +1,9 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { - pub namespace: Namespace, direct: Vec<&'a Api>, nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } @@ -47,17 +45,7 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) .collect(); - let namespace = apis - .first() - .copied() - .and_then(Api::namespace) - .map_or(Namespace::ROOT, |ns| ns.iter().take(depth).collect()); - - NamespaceEntries { - namespace, - direct, - nested, - } + NamespaceEntries { direct, nested } } #[cfg(test)] From b8f3f5474cdd56daf0959658dddf0c2a1a31caad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:54:58 +0000 Subject: [PATCH 1207/2232] Merge pull request #404 from dtolnay/namespace Move set_namespace to individual items --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 08ce4b4..7309aa9 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -1,11 +1,9 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::Api; use proc_macro2::Ident; use std::collections::BTreeMap; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { - pub namespace: Namespace, direct: Vec<&'a Api>, nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, } @@ -47,17 +45,7 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) .collect(); - let namespace = apis - .first() - .copied() - .and_then(Api::namespace) - .map_or(Namespace::ROOT, |ns| ns.iter().take(depth).collect()); - - NamespaceEntries { - namespace, - direct, - nested, - } + NamespaceEntries { direct, nested } } #[cfg(test)] diff --git a/gen/src/write.rs b/gen/src/write.rs index abc965a..0369ef5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -54,7 +54,6 @@ fn write_namespace_forward_declarations(out: &mut OutFile, ns_entries: &Namespac } fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { - out.set_namespace(&ns_entries.namespace); let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -182,6 +181,7 @@ fn pick_includes_and_builtins(out: &mut OutFile) { } fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { + out.set_namespace(&strct.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -216,6 +216,7 @@ fn write_struct_with_methods<'a>( ety: &'a ExternType, methods: &[&ExternFn], ) { + out.set_namespace(&ety.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -241,6 +242,7 @@ fn write_struct_with_methods<'a>( } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + out.set_namespace(&enm.ident.cxx.namespace); let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); @@ -258,6 +260,7 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + out.set_namespace(&enm.ident.cxx.namespace); write!( out, "static_assert(sizeof({}) == sizeof(", @@ -319,6 +322,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.next_section(); + out.set_namespace(&efn.ident.cxx.namespace); out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); @@ -520,6 +524,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { + out.set_namespace(&efn.ident.cxx.namespace); out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -578,6 +583,7 @@ fn write_rust_function_decl_impl( } fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { + out.set_namespace(&efn.ident.cxx.namespace); for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } From 1b0339c16b00f4f874418bbf620ad425db45c60e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 04:55:07 +0000 Subject: [PATCH 1208/2232] Emit all data structures before all functions --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 0369ef5..8a4133c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -20,7 +20,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { +fn write_namespace_data_structures<'a>( + out: &mut OutFile<'a>, + ns_entries: &'a NamespaceEntries<'a>, +) { let apis = ns_entries.direct_content(); let mut methods_for_type = HashMap::new(); @@ -103,6 +107,14 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace } } + for (_, nested_ns_entries) in ns_entries.nested_content() { + write_namespace_data_structures(out, nested_ns_entries); + } +} + +fn write_namespace_functions<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { + let apis = ns_entries.direct_content(); + if !out.header { for api in apis { match api { @@ -121,7 +133,7 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace } for (_, nested_ns_entries) in ns_entries.nested_content() { - write_namespace_contents(out, nested_ns_entries); + write_namespace_functions(out, nested_ns_entries); } } From e1109d93a1959151386b72d21c98af6b787525fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:01:20 +0000 Subject: [PATCH 1209/2232] Emit data structures and functions each in source order --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 8a4133c..7753ebf 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -20,8 +20,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec( - out: &mut OutFile<'a>, - ns_entries: &'a NamespaceEntries<'a>, -) { - let apis = ns_entries.direct_content(); - +fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { let mut methods_for_type = HashMap::new(); for api in apis { if let Api::RustFunction(efn) = api { @@ -106,15 +101,9 @@ fn write_namespace_data_structures<'a>( } } } - - for (_, nested_ns_entries) in ns_entries.nested_content() { - write_namespace_data_structures(out, nested_ns_entries); - } } -fn write_namespace_functions<'a>(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { - let apis = ns_entries.direct_content(); - +fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { if !out.header { for api in apis { match api { @@ -131,10 +120,6 @@ fn write_namespace_functions<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespac write_rust_function_shim(out, efn); } } - - for (_, nested_ns_entries) in ns_entries.nested_content() { - write_namespace_functions(out, nested_ns_entries); - } } fn pick_includes_and_builtins(out: &mut OutFile) { From eed76fa82ac5e8f7c29b85cdcf2785f32aeed902 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:07:08 +0000 Subject: [PATCH 1210/2232] Merge pull request #405 from dtolnay/sort Emit data structures and functions in source order --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 0369ef5..7753ebf 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -20,7 +20,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec(out: &mut OutFile<'a>, ns_entries: &'a NamespaceEntries<'a>) { - let apis = ns_entries.direct_content(); - +fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { let mut methods_for_type = HashMap::new(); for api in apis { if let Api::RustFunction(efn) = api { @@ -102,7 +101,9 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace } } } +} +fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { if !out.header { for api in apis { match api { @@ -119,10 +120,6 @@ fn write_namespace_contents<'a>(out: &mut OutFile<'a>, ns_entries: &'a Namespace write_rust_function_shim(out, efn); } } - - for (_, nested_ns_entries) in ns_entries.nested_content() { - write_namespace_contents(out, nested_ns_entries); - } } fn pick_includes_and_builtins(out: &mut OutFile) { From 169bb47773b665105b30573499f2ce6c4323820f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:07:17 +0000 Subject: [PATCH 1211/2232] Clean up control flow of write::gen --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 7753ebf..f5ef807 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -22,11 +22,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec Symbol { } fn write_generic_instantiations(out: &mut OutFile) { + if out.header { + return; + } + + out.next_section(); out.set_namespace(Default::default()); out.begin_block(Block::ExternC); for ty in out.types { From 0472c262bcdf0631460c23caefa97963667d8c23 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:30:30 +0000 Subject: [PATCH 1212/2232] Flatten a nested if-let --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 7309aa9..18f2896 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -27,15 +27,12 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut direct = Vec::new(); let mut nested_namespaces = BTreeMap::new(); for api in &apis { - if let Some(namespace) = api.namespace() { - let first_ns_elem = namespace.iter().nth(depth); - if let Some(first_ns_elem) = first_ns_elem { - nested_namespaces - .entry(first_ns_elem) - .or_insert_with(Vec::new) - .push(*api); - continue; - } + if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) { + nested_namespaces + .entry(first_ns_elem) + .or_insert_with(Vec::new) + .push(*api); + continue; } direct.push(*api); } From 8704e3ed14eada73216eb55ac226011e48479556 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:33:25 +0000 Subject: [PATCH 1213/2232] Preserve source order of namespaces for forward declaration This enables us to avoid answering hard questions like whether 'foo10' is less or greater than 'foo9'. --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 18f2896..748ec3f 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -1,11 +1,11 @@ use crate::syntax::Api; use proc_macro2::Ident; -use std::collections::BTreeMap; +use std::collections::HashMap as Map; use std::iter::FromIterator; pub struct NamespaceEntries<'a> { direct: Vec<&'a Api>, - nested: BTreeMap<&'a Ident, NamespaceEntries<'a>>, + nested: Vec<(&'a Ident, NamespaceEntries<'a>)>, } impl<'a> NamespaceEntries<'a> { @@ -25,13 +25,18 @@ impl<'a> NamespaceEntries<'a> { fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut direct = Vec::new(); - let mut nested_namespaces = BTreeMap::new(); + let mut nested_namespaces = Vec::new(); + let mut index_of_namespace = Map::new(); + for api in &apis { if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) { - nested_namespaces - .entry(first_ns_elem) - .or_insert_with(Vec::new) - .push(*api); + match index_of_namespace.get(first_ns_elem) { + None => { + index_of_namespace.insert(first_ns_elem, nested_namespaces.len()); + nested_namespaces.push((first_ns_elem, vec![*api])); + } + Some(&index) => nested_namespaces[index].1.push(*api), + } continue; } direct.push(*api); From de4b55392752ae3688b06ce326f04bd541d1e4f7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 05:37:25 +0000 Subject: [PATCH 1214/2232] Update test_ns_entries_sort to match source order sorting --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs index 748ec3f..143f6ec 100644 --- a/gen/src/alphasort.rs +++ b/gen/src/alphasort.rs @@ -83,12 +83,21 @@ mod tests { assert_ident(root_direct[2], "B"); let mut root_nested = root.nested_content(); - let (id, d) = root_nested.next().unwrap(); - assert_eq!(id, "D"); let (id, g) = root_nested.next().unwrap(); assert_eq!(id, "G"); + let (id, d) = root_nested.next().unwrap(); + assert_eq!(id, "D"); assert!(root_nested.next().is_none()); + // ::G + let g_direct = g.direct_content(); + assert_eq!(g_direct.len(), 2); + assert_ident(g_direct[0], "E"); + assert_ident(g_direct[1], "H"); + + let mut g_nested = g.nested_content(); + assert!(g_nested.next().is_none()); + // ::D let d_direct = d.direct_content(); assert_eq!(d_direct.len(), 3); @@ -105,15 +114,6 @@ mod tests { assert_eq!(k_direct.len(), 2); assert_ident(k_direct[0], "L"); assert_ident(k_direct[1], "M"); - - // ::G - let g_direct = g.direct_content(); - assert_eq!(g_direct.len(), 2); - assert_ident(g_direct[0], "E"); - assert_ident(g_direct[1], "H"); - - let mut g_nested = g.nested_content(); - assert!(g_nested.next().is_none()); } fn assert_ident(api: &Api, expected: &str) { From f7b81fb845a0aaa4238653af9f9a5624b290bfcb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 06:39:22 +0000 Subject: [PATCH 1215/2232] Move NamespaceEntries to nested:: module Since the way we sort nested namespaces is no longer based on alphabetic order. --- diff --git a/gen/src/alphasort.rs b/gen/src/alphasort.rs deleted file mode 100644 index 143f6ec..0000000 --- a/gen/src/alphasort.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::syntax::Api; -use proc_macro2::Ident; -use std::collections::HashMap as Map; -use std::iter::FromIterator; - -pub struct NamespaceEntries<'a> { - direct: Vec<&'a Api>, - nested: Vec<(&'a Ident, NamespaceEntries<'a>)>, -} - -impl<'a> NamespaceEntries<'a> { - pub fn new(apis: &'a [Api]) -> Self { - let api_refs = Vec::from_iter(apis); - sort_by_inner_namespace(api_refs, 0) - } - - pub fn direct_content(&self) -> &[&'a Api] { - &self.direct - } - - pub fn nested_content(&self) -> impl Iterator)> { - self.nested.iter().map(|(k, entries)| (*k, entries)) - } -} - -fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { - let mut direct = Vec::new(); - let mut nested_namespaces = Vec::new(); - let mut index_of_namespace = Map::new(); - - for api in &apis { - if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) { - match index_of_namespace.get(first_ns_elem) { - None => { - index_of_namespace.insert(first_ns_elem, nested_namespaces.len()); - nested_namespaces.push((first_ns_elem, vec![*api])); - } - Some(&index) => nested_namespaces[index].1.push(*api), - } - continue; - } - direct.push(*api); - } - - let nested = nested_namespaces - .into_iter() - .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) - .collect(); - - NamespaceEntries { direct, nested } -} - -#[cfg(test)] -mod tests { - use super::NamespaceEntries; - use crate::syntax::namespace::Namespace; - use crate::syntax::{Api, Doc, ExternType, Pair}; - use proc_macro2::{Ident, Span}; - use syn::Token; - - #[test] - fn test_ns_entries_sort() { - let apis = &[ - make_api(None, "C"), - make_api(None, "A"), - make_api(Some("G"), "E"), - make_api(Some("D"), "F"), - make_api(Some("G"), "H"), - make_api(Some("D::K"), "L"), - make_api(Some("D::K"), "M"), - make_api(None, "B"), - make_api(Some("D"), "I"), - make_api(Some("D"), "J"), - ]; - - let root = NamespaceEntries::new(apis); - - // :: - let root_direct = root.direct_content(); - assert_eq!(root_direct.len(), 3); - assert_ident(root_direct[0], "C"); - assert_ident(root_direct[1], "A"); - assert_ident(root_direct[2], "B"); - - let mut root_nested = root.nested_content(); - let (id, g) = root_nested.next().unwrap(); - assert_eq!(id, "G"); - let (id, d) = root_nested.next().unwrap(); - assert_eq!(id, "D"); - assert!(root_nested.next().is_none()); - - // ::G - let g_direct = g.direct_content(); - assert_eq!(g_direct.len(), 2); - assert_ident(g_direct[0], "E"); - assert_ident(g_direct[1], "H"); - - let mut g_nested = g.nested_content(); - assert!(g_nested.next().is_none()); - - // ::D - let d_direct = d.direct_content(); - assert_eq!(d_direct.len(), 3); - assert_ident(d_direct[0], "F"); - assert_ident(d_direct[1], "I"); - assert_ident(d_direct[2], "J"); - - let mut d_nested = d.nested_content(); - let (id, k) = d_nested.next().unwrap(); - assert_eq!(id, "K"); - - // ::D::K - let k_direct = k.direct_content(); - assert_eq!(k_direct.len(), 2); - assert_ident(k_direct[0], "L"); - assert_ident(k_direct[1], "M"); - } - - fn assert_ident(api: &Api, expected: &str) { - if let Api::CxxType(cxx_type) = api { - assert_eq!(cxx_type.ident.cxx.ident, expected); - } else { - unreachable!() - } - } - - fn make_api(ns: Option<&str>, ident: &str) -> Api { - let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap()); - Api::CxxType(ExternType { - doc: Doc::new(), - type_token: Token![type](Span::call_site()), - ident: Pair::new(ns, Ident::new(ident, Span::call_site())), - semi_token: Token![;](Span::call_site()), - trusted: false, - }) - } -} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 8626058..61a8bbe 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -1,7 +1,6 @@ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. -mod alphasort; mod block; mod builtin; mod check; @@ -10,6 +9,7 @@ mod file; pub(super) mod fs; mod ifndef; pub(super) mod include; +mod nested; pub(super) mod out; mod write; diff --git a/gen/src/nested.rs b/gen/src/nested.rs new file mode 100644 index 0000000..143f6ec --- /dev/null +++ b/gen/src/nested.rs @@ -0,0 +1,137 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::HashMap as Map; +use std::iter::FromIterator; + +pub struct NamespaceEntries<'a> { + direct: Vec<&'a Api>, + nested: Vec<(&'a Ident, NamespaceEntries<'a>)>, +} + +impl<'a> NamespaceEntries<'a> { + pub fn new(apis: &'a [Api]) -> Self { + let api_refs = Vec::from_iter(apis); + sort_by_inner_namespace(api_refs, 0) + } + + pub fn direct_content(&self) -> &[&'a Api] { + &self.direct + } + + pub fn nested_content(&self) -> impl Iterator)> { + self.nested.iter().map(|(k, entries)| (*k, entries)) + } +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut direct = Vec::new(); + let mut nested_namespaces = Vec::new(); + let mut index_of_namespace = Map::new(); + + for api in &apis { + if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) { + match index_of_namespace.get(first_ns_elem) { + None => { + index_of_namespace.insert(first_ns_elem, nested_namespaces.len()); + nested_namespaces.push((first_ns_elem, vec![*api])); + } + Some(&index) => nested_namespaces[index].1.push(*api), + } + continue; + } + direct.push(*api); + } + + let nested = nested_namespaces + .into_iter() + .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) + .collect(); + + NamespaceEntries { direct, nested } +} + +#[cfg(test)] +mod tests { + use super::NamespaceEntries; + use crate::syntax::namespace::Namespace; + use crate::syntax::{Api, Doc, ExternType, Pair}; + use proc_macro2::{Ident, Span}; + use syn::Token; + + #[test] + fn test_ns_entries_sort() { + let apis = &[ + make_api(None, "C"), + make_api(None, "A"), + make_api(Some("G"), "E"), + make_api(Some("D"), "F"), + make_api(Some("G"), "H"), + make_api(Some("D::K"), "L"), + make_api(Some("D::K"), "M"), + make_api(None, "B"), + make_api(Some("D"), "I"), + make_api(Some("D"), "J"), + ]; + + let root = NamespaceEntries::new(apis); + + // :: + let root_direct = root.direct_content(); + assert_eq!(root_direct.len(), 3); + assert_ident(root_direct[0], "C"); + assert_ident(root_direct[1], "A"); + assert_ident(root_direct[2], "B"); + + let mut root_nested = root.nested_content(); + let (id, g) = root_nested.next().unwrap(); + assert_eq!(id, "G"); + let (id, d) = root_nested.next().unwrap(); + assert_eq!(id, "D"); + assert!(root_nested.next().is_none()); + + // ::G + let g_direct = g.direct_content(); + assert_eq!(g_direct.len(), 2); + assert_ident(g_direct[0], "E"); + assert_ident(g_direct[1], "H"); + + let mut g_nested = g.nested_content(); + assert!(g_nested.next().is_none()); + + // ::D + let d_direct = d.direct_content(); + assert_eq!(d_direct.len(), 3); + assert_ident(d_direct[0], "F"); + assert_ident(d_direct[1], "I"); + assert_ident(d_direct[2], "J"); + + let mut d_nested = d.nested_content(); + let (id, k) = d_nested.next().unwrap(); + assert_eq!(id, "K"); + + // ::D::K + let k_direct = k.direct_content(); + assert_eq!(k_direct.len(), 2); + assert_ident(k_direct[0], "L"); + assert_ident(k_direct[1], "M"); + } + + fn assert_ident(api: &Api, expected: &str) { + if let Api::CxxType(cxx_type) = api { + assert_eq!(cxx_type.ident.cxx.ident, expected); + } else { + unreachable!() + } + } + + fn make_api(ns: Option<&str>, ident: &str) -> Api { + let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap()); + Api::CxxType(ExternType { + doc: Doc::new(), + type_token: Token![type](Span::call_site()), + ident: Pair::new(ns, Ident::new(ident, Span::call_site())), + semi_token: Token![;](Span::call_site()), + trusted: false, + }) + } +} diff --git a/gen/src/write.rs b/gen/src/write.rs index f5ef807..a42d03e 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,5 +1,5 @@ -use crate::gen::alphasort::NamespaceEntries; use crate::gen::block::Block; +use crate::gen::nested::NamespaceEntries; use crate::gen::out::OutFile; use crate::gen::{builtin, include, Opt}; use crate::syntax::atom::Atom::{self, *}; From 6edeaab3cb21824c0e24c9c34e50824ca1cd45ed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 06:48:37 +0000 Subject: [PATCH 1216/2232] Treat un-namespaced items the same as root namespace --- diff --git a/gen/src/nested.rs b/gen/src/nested.rs index 143f6ec..8a1dd6d 100644 --- a/gen/src/nested.rs +++ b/gen/src/nested.rs @@ -29,7 +29,7 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut index_of_namespace = Map::new(); for api in &apis { - if let Some(first_ns_elem) = api.namespace().and_then(|ns| ns.iter().nth(depth)) { + if let Some(first_ns_elem) = api.namespace().iter().nth(depth) { match index_of_namespace.get(first_ns_elem) { None => { index_of_namespace.insert(first_ns_elem, nested_namespaces.len()); diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 02e5dc2..160650f 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -86,13 +86,13 @@ impl<'a> FromIterator<&'a Ident> for Namespace { } impl Api { - pub fn namespace(&self) -> Option<&Namespace> { + pub fn namespace(&self) -> &Namespace { match self { - Api::CxxFunction(efn) | Api::RustFunction(efn) => Some(&efn.ident.cxx.namespace), - Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident.cxx.namespace), - Api::Enum(enm) => Some(&enm.ident.cxx.namespace), - Api::Struct(strct) => Some(&strct.ident.cxx.namespace), - Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => None, + Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.cxx.namespace, + Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.cxx.namespace, + Api::Enum(enm) => &enm.ident.cxx.namespace, + Api::Struct(strct) => &strct.ident.cxx.namespace, + Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), } } } From 90b133b5532a29b99a887fcad2f9327956079bcd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 06:53:37 +0000 Subject: [PATCH 1217/2232] Move C++-specific namespace code unused by the proc macro --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 61a8bbe..3d12c71 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -9,6 +9,7 @@ mod file; pub(super) mod fs; mod ifndef; pub(super) mod include; +mod namespace; mod nested; pub(super) mod out; mod write; diff --git a/gen/src/namespace.rs b/gen/src/namespace.rs new file mode 100644 index 0000000..a8b049f --- /dev/null +++ b/gen/src/namespace.rs @@ -0,0 +1,14 @@ +use crate::syntax::namespace::Namespace; +use crate::syntax::Api; + +impl Api { + pub fn namespace(&self) -> &Namespace { + match self { + Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.cxx.namespace, + Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.cxx.namespace, + Api::Enum(enm) => &enm.ident.cxx.namespace, + Api::Struct(strct) => &strct.ident.cxx.namespace, + Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), + } + } +} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 160650f..07185e1 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -1,5 +1,4 @@ use crate::syntax::qualified::QualifiedName; -use crate::syntax::Api; use quote::IdentFragment; use std::fmt::{self, Display}; use std::iter::FromIterator; @@ -84,15 +83,3 @@ impl<'a> FromIterator<&'a Ident> for Namespace { Namespace { segments } } } - -impl Api { - pub fn namespace(&self) -> &Namespace { - match self { - Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.cxx.namespace, - Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.cxx.namespace, - Api::Enum(enm) => &enm.ident.cxx.namespace, - Api::Struct(strct) => &strct.ident.cxx.namespace, - Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), - } - } -} From 7b0e510029beca0b255267470c8f2123520db0d3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 07:51:41 +0000 Subject: [PATCH 1218/2232] Omit namespaces containing no forward declarations --- diff --git a/gen/src/nested.rs b/gen/src/nested.rs index 8a1dd6d..0a8816c 100644 --- a/gen/src/nested.rs +++ b/gen/src/nested.rs @@ -1,7 +1,6 @@ use crate::syntax::Api; use proc_macro2::Ident; use std::collections::HashMap as Map; -use std::iter::FromIterator; pub struct NamespaceEntries<'a> { direct: Vec<&'a Api>, @@ -9,9 +8,8 @@ pub struct NamespaceEntries<'a> { } impl<'a> NamespaceEntries<'a> { - pub fn new(apis: &'a [Api]) -> Self { - let api_refs = Vec::from_iter(apis); - sort_by_inner_namespace(api_refs, 0) + pub fn new(apis: Vec<&'a Api>) -> Self { + sort_by_inner_namespace(apis, 0) } pub fn direct_content(&self) -> &[&'a Api] { @@ -56,6 +54,7 @@ mod tests { use crate::syntax::namespace::Namespace; use crate::syntax::{Api, Doc, ExternType, Pair}; use proc_macro2::{Ident, Span}; + use std::iter::FromIterator; use syn::Token; #[test] @@ -73,7 +72,7 @@ mod tests { make_api(Some("D"), "J"), ]; - let root = NamespaceEntries::new(apis); + let root = NamespaceEntries::new(Vec::from_iter(apis)); // :: let root_direct = root.direct_content(); diff --git a/gen/src/write.rs b/gen/src/write.rs index a42d03e..b2046c5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,8 +18,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec Vec true, + _ => false, + }; - for api in apis { - match api { - Api::Include(include) => out.include.insert(include), - Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), - Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), - Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), - _ => {} + let apis_by_namespace = + NamespaceEntries::new(apis.iter().filter(needs_forward_declaration).collect()); + + write(out, &apis_by_namespace); + + fn write(out: &mut OutFile, ns_entries: &NamespaceEntries) { + let apis = ns_entries.direct_content(); + + for api in apis { + match api { + Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), + _ => unreachable!(), + } } - } - for (namespace, nested_ns_entries) in ns_entries.nested_content() { - writeln!(out, "namespace {} {{", namespace); - write_namespace_forward_declarations(out, nested_ns_entries); - writeln!(out, "}} // namespace {}", namespace); + for (namespace, nested_ns_entries) in ns_entries.nested_content() { + writeln!(out, "namespace {} {{", namespace); + write(out, nested_ns_entries); + writeln!(out, "}} // namespace {}", namespace); + } } } @@ -65,6 +75,7 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { for api in apis { match api { + Api::Include(include) => out.include.insert(include), Api::Struct(strct) => { out.next_section(); if !out.types.cxx.contains(&strct.ident.rust) { From d920be59083e03020459fe39ae31bf435c9b5850 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 07:51:41 +0000 Subject: [PATCH 1219/2232] Indent forward declarations by namespace depth --- diff --git a/gen/src/write.rs b/gen/src/write.rs index b2046c5..9d07173 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -38,12 +38,13 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { let apis_by_namespace = NamespaceEntries::new(apis.iter().filter(needs_forward_declaration).collect()); - write(out, &apis_by_namespace); + write(out, &apis_by_namespace, 0); - fn write(out: &mut OutFile, ns_entries: &NamespaceEntries) { + fn write(out: &mut OutFile, ns_entries: &NamespaceEntries, indent: usize) { let apis = ns_entries.direct_content(); for api in apis { + write!(out, "{:1$}", "", indent); match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), @@ -53,9 +54,9 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { } for (namespace, nested_ns_entries) in ns_entries.nested_content() { - writeln!(out, "namespace {} {{", namespace); - write(out, nested_ns_entries); - writeln!(out, "}} // namespace {}", namespace); + writeln!(out, "{:2$}namespace {} {{", "", namespace, indent); + write(out, nested_ns_entries, indent + 2); + writeln!(out, "{:1$}}}", "", indent); } } } From 0e3ee8e398ebd17e0b68768c7587ac5cfd56d126 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 07:51:41 +0000 Subject: [PATCH 1220/2232] Implement forward declaration for enums --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 9d07173..ebef728 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -31,7 +31,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec true, + Api::Struct(_) | Api::Enum(_) | Api::CxxType(_) | Api::RustType(_) => true, _ => false, }; @@ -47,6 +47,7 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { write!(out, "{:1$}", "", indent); match api { Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), + Api::Enum(enm) => write_enum_decl(out, enm), Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), _ => unreachable!(), @@ -207,6 +208,12 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } +fn write_enum_decl(out: &mut OutFile, enm: &Enum) { + write!(out, "enum class {} : ", enm.ident.cxx.ident); + write_atom(out, enm.repr); + writeln!(out, ";"); +} + fn write_struct_using(out: &mut OutFile, ident: &CppName) { writeln!( out, From 4bfca11c1e2504364fe277b54bfc0c875ff9ef5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 07:59:11 +0000 Subject: [PATCH 1221/2232] Fix duplicated forward declaration for extern enums --- diff --git a/gen/src/write.rs b/gen/src/write.rs index ebef728..68b5468 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -31,7 +31,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec true, + Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true, + Api::Enum(enm) => !out.types.cxx.contains(&enm.ident.rust), _ => false, }; From dfb82d74d5e78af406b04c93eecd0499fcf765c1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:10:15 +0000 Subject: [PATCH 1222/2232] Move Api::Include handling to pick_includes_and_builtins --- diff --git a/gen/src/write.rs b/gen/src/write.rs index 68b5468..2967594 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -15,7 +15,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec(out: &mut OutFile<'a>, apis: &'a [Api]) { for api in apis { match api { - Api::Include(include) => out.include.insert(include), Api::Struct(strct) => { out.next_section(); if !out.types.cxx.contains(&strct.ident.rust) { @@ -132,7 +131,13 @@ fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { } } -fn pick_includes_and_builtins(out: &mut OutFile) { +fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { + for api in apis { + if let Api::Include(include) = api { + out.include.insert(include); + } + } + for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { From 58cd0c75b48457408a245fed507363fe86a80edd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:10:37 +0000 Subject: [PATCH 1223/2232] Skeleton for topological sort --- diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 3d12c71..d19dea0 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -12,6 +12,7 @@ pub(super) mod include; mod namespace; mod nested; pub(super) mod out; +mod toposort; mod write; pub(super) use self::error::Error; diff --git a/gen/src/toposort.rs b/gen/src/toposort.rs new file mode 100644 index 0000000..97b10f9 --- /dev/null +++ b/gen/src/toposort.rs @@ -0,0 +1,8 @@ +use crate::syntax::{Api, Types}; +use std::iter::FromIterator; + +pub fn sort<'a>(apis: &'a [Api], types: &Types) -> Vec<&'a Api> { + // TODO https://github.com/dtolnay/cxx/issues/292 + let _ = types; + Vec::from_iter(apis) +} diff --git a/gen/src/write.rs b/gen/src/write.rs index 2967594..1cd6d07 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,7 +1,7 @@ use crate::gen::block::Block; use crate::gen::nested::NamespaceEntries; use crate::gen::out::OutFile; -use crate::gen::{builtin, include, Opt}; +use crate::gen::{builtin, include, toposort, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ @@ -76,7 +76,7 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { } } - for api in apis { + for api in toposort::sort(apis, out.types) { match api { Api::Struct(strct) => { out.next_section(); From 8faec77e12aa6e4da2eb120d39a18adae5d2989b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:31:34 +0000 Subject: [PATCH 1224/2232] Move CppName's namespace into Pair --- diff --git a/gen/src/namespace.rs b/gen/src/namespace.rs index a8b049f..7343875 100644 --- a/gen/src/namespace.rs +++ b/gen/src/namespace.rs @@ -4,10 +4,10 @@ use crate::syntax::Api; impl Api { pub fn namespace(&self) -> &Namespace { match self { - Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.cxx.namespace, - Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.cxx.namespace, - Api::Enum(enm) => &enm.ident.cxx.namespace, - Api::Struct(strct) => &strct.ident.cxx.namespace, + Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.namespace, + Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.namespace, + Api::Enum(enm) => &enm.ident.namespace, + Api::Struct(strct) => &strct.ident.namespace, Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), } } diff --git a/gen/src/write.rs b/gen/src/write.rs index 1cd6d07..7c16e81 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -5,8 +5,8 @@ use crate::gen::{builtin, include, toposort, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::symbol::Symbol; use crate::syntax::{ - mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type, - Types, Var, + mangle, Api, Enum, ExternFn, ExternType, Pair, ResolvableName, Signature, Struct, Type, Types, + Var, }; use proc_macro2::Ident; use std::collections::HashMap; @@ -47,10 +47,10 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { for api in apis { write!(out, "{:1$}", "", indent); match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident), + Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx), Api::Enum(enm) => write_enum_decl(out, enm), - Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx), - Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident), + Api::CxxType(ety) => write_struct_using(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx), _ => unreachable!(), } } @@ -106,7 +106,7 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { for api in apis { if let Api::TypeAlias(ety) = api { if out.types.required_trivial.contains_key(&ety.ident.rust) { - check_trivial_extern_type(out, &ety.ident.cxx) + check_trivial_extern_type(out, &ety.ident) } } } @@ -193,14 +193,14 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { - out.set_namespace(&strct.ident.cxx.namespace); - let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol()); + out.set_namespace(&strct.ident.namespace); + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident.cxx.ident); + writeln!(out, "struct {} final {{", strct.ident.cxx); for field in &strct.fields { write!(out, " "); write_type_space(out, &field.ty); @@ -215,18 +215,13 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { } fn write_enum_decl(out: &mut OutFile, enm: &Enum) { - write!(out, "enum class {} : ", enm.ident.cxx.ident); + write!(out, "enum class {} : ", enm.ident.cxx); write_atom(out, enm.repr); writeln!(out, ";"); } -fn write_struct_using(out: &mut OutFile, ident: &CppName) { - writeln!( - out, - "using {} = {};", - ident.ident, - ident.to_fully_qualified() - ); +fn write_struct_using(out: &mut OutFile, ident: &Pair) { + writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified()); } fn write_struct_with_methods<'a>( @@ -234,24 +229,24 @@ fn write_struct_with_methods<'a>( ety: &'a ExternType, methods: &[&ExternFn], ) { - out.set_namespace(&ety.ident.cxx.namespace); - let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol()); + out.set_namespace(&ety.ident.namespace); + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident.cxx.ident); - writeln!(out, " {}() = delete;", ety.ident.cxx.ident); + writeln!(out, "struct {} final {{", ety.ident.cxx); + writeln!(out, " {}() = delete;", ety.ident.cxx); writeln!( out, " {}(const {} &) = delete;", - ety.ident.cxx.ident, ety.ident.cxx.ident + ety.ident.cxx, ety.ident.cxx, ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.ident.to_string(); + let local_name = method.ident.cxx.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -260,14 +255,14 @@ fn write_struct_with_methods<'a>( } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { - out.set_namespace(&enm.ident.cxx.namespace); - let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol()); + out.set_namespace(&enm.ident.namespace); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident.cxx.ident); + write!(out, "enum class {} : ", enm.ident.cxx); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -278,12 +273,8 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { - out.set_namespace(&enm.ident.cxx.namespace); - write!( - out, - "static_assert(sizeof({}) == sizeof(", - enm.ident.cxx.ident - ); + out.set_namespace(&enm.ident.namespace); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident.cxx); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -292,12 +283,12 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident.cxx.ident, variant.ident, variant.discriminant, + enm.ident.cxx, variant.ident, variant.discriminant, ); } } -fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { +fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) { // NOTE: The following two static assertions are just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: @@ -340,7 +331,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) { fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.next_section(); - out.set_namespace(&efn.ident.cxx.namespace); + out.set_namespace(&efn.ident.namespace); out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); @@ -360,7 +351,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write!( out, "{} &self", - out.types.resolve(&receiver.ty).to_fully_qualified() + out.types.resolve(&receiver.ty).to_fully_qualified(), ); } for (i, arg) in efn.args.iter().enumerate() { @@ -391,7 +382,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out, "({}::*{}$)(", out.types.resolve(&receiver.ty).to_fully_qualified(), - efn.ident.rust + efn.ident.rust, ), } for (i, arg) in efn.args.iter().enumerate() { @@ -408,12 +399,12 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()), + None => write!(out, "{}", efn.ident.to_fully_qualified()), Some(receiver) => write!( out, "&{}::{}", out.types.resolve(&receiver.ty).to_fully_qualified(), - efn.ident.cxx.ident + efn.ident.cxx, ), } writeln!(out, ";"); @@ -542,7 +533,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { - out.set_namespace(&efn.ident.cxx.namespace); + out.set_namespace(&efn.ident.namespace); out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -572,7 +563,7 @@ fn write_rust_function_decl_impl( write!( out, "{} &self", - out.types.resolve(&receiver.ty).to_fully_qualified() + out.types.resolve(&receiver.ty).to_fully_qualified(), ); needs_comma = true; } @@ -601,17 +592,13 @@ fn write_rust_function_decl_impl( } fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { - out.set_namespace(&efn.ident.cxx.namespace); + out.set_namespace(&efn.ident.namespace); for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.ident.to_string(), - Some(receiver) => format!( - "{}::{}", - out.types.resolve(&receiver.ty).ident, - efn.ident.cxx.ident - ), + None => efn.ident.cxx.to_string(), + Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.ident.cxx), }; let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -1041,7 +1028,7 @@ fn write_generic_instantiations(out: &mut OutFile) { out.end_block(Block::Namespace("rust")); } -fn write_rust_box_extern(out: &mut OutFile, ident: &CppName) { +fn write_rust_box_extern(out: &mut OutFile, ident: &Pair) { let inner = ident.to_fully_qualified(); let instance = ident.to_symbol(); @@ -1095,7 +1082,7 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName) { writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } -fn write_rust_box_impl(out: &mut OutFile, ident: &CppName) { +fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) { let inner = ident.to_fully_qualified(); let instance = ident.to_symbol(); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index b25d830..026ec43 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -4,7 +4,7 @@ use crate::syntax::file::Module; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, CppName, Enum, ExternFn, ExternType, Impl, ResolvableName, Signature, + self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Signature, Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; @@ -125,11 +125,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } fn expand_struct(strct: &Struct) -> TokenStream { - let ident = &strct.ident.rust; - let cxx_ident = &strct.ident.cxx; + let ident = &strct.ident; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(cxx_ident); + let type_id = type_id(&strct.ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -153,11 +152,10 @@ fn expand_struct(strct: &Struct) -> TokenStream { } fn expand_enum(enm: &Enum) -> TokenStream { - let ident = &enm.ident.rust; - let cxx_ident = &enm.ident.cxx; + let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(cxx_ident); + let type_id = type_id(&enm.ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -187,10 +185,9 @@ fn expand_enum(enm: &Enum) -> TokenStream { } fn expand_cxx_type(ety: &ExternType) -> TokenStream { - let ident = &ety.ident.rust; - let cxx_ident = &ety.ident.cxx; + let ident = &ety.ident; let doc = &ety.doc; - let type_id = type_id(&cxx_ident); + let type_id = type_id(&ety.ident); quote! { #doc @@ -423,7 +420,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { if unsafety.is_none() { dispatch = quote!(unsafe { #dispatch }); } - let ident = &efn.ident.rust; + let ident = &efn.ident; let function_shim = quote! { #doc pub #unsafety fn #ident(#(#all_args,)*) #ret { @@ -688,7 +685,7 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.ident; - let type_id = type_id(&ident.cxx); + let type_id = type_id(ident); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); @@ -708,8 +705,8 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { verify } -fn type_id(ident: &CppName) -> TokenStream { - let path = ident.to_fully_qualified(); +fn type_id(name: &Pair) -> TokenStream { + let path = name.to_fully_qualified(); quote! { ::cxx::type_id!(#path) } diff --git a/syntax/ident.rs b/syntax/ident.rs index 03c4380..bb2fe46 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,5 +1,5 @@ use crate::syntax::check::Check; -use crate::syntax::{error, Api, CppName}; +use crate::syntax::{error, Api, Pair}; use proc_macro2::Ident; fn check(cx: &mut Check, ident: &Ident) { @@ -12,11 +12,11 @@ fn check(cx: &mut Check, ident: &Ident) { } } -fn check_ident(cx: &mut Check, ident: &CppName) { - for segment in &ident.namespace { +fn check_ident(cx: &mut Check, name: &Pair) { + for segment in &name.namespace { check(cx, segment); } - check(cx, &ident.ident); + check(cx, &name.cxx); } pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { @@ -24,19 +24,19 @@ pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check_ident(cx, &strct.ident.cxx); + check_ident(cx, &strct.ident); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check_ident(cx, &enm.ident.cxx); + check_ident(cx, &enm.ident); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check_ident(cx, &ety.ident.cxx); + check_ident(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.ident.rust); @@ -45,7 +45,7 @@ pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { } } Api::TypeAlias(alias) => { - check_ident(cx, &alias.ident.cxx); + check_ident(cx, &alias.ident); } } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index d1f3adc..55ffca5 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -15,13 +15,13 @@ pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { Some(receiver) => { let receiver_ident = types.resolve(&receiver.ty); join!( - efn.ident.cxx.namespace, + efn.ident.namespace, CXXBRIDGE, - receiver_ident.ident, + receiver_ident.cxx, efn.ident.rust ) } - None => join!(efn.ident.cxx.namespace, CXXBRIDGE, efn.ident.rust), + None => join!(efn.ident.namespace, CXXBRIDGE, efn.ident.rust), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index b12d72b..6684b6a 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -188,17 +188,9 @@ pub enum Lang { // qualified C++ name. #[derive(Clone)] pub struct Pair { - pub cxx: CppName, - pub rust: Ident, -} - -// A C++ identifier in a particular namespace. It is intentional that this does -// not impl Display, because we want to force users actively to decide whether -// to output it as a qualified name or as an unqualfied name. -#[derive(Clone)] -pub struct CppName { pub namespace: Namespace, - pub ident: Ident, + pub cxx: Ident, + pub rust: Ident, } // Wrapper for a type which needs to be resolved before it can be printed in diff --git a/syntax/names.rs b/syntax/names.rs index ee010a9..25ae9f4 100644 --- a/syntax/names.rs +++ b/syntax/names.rs @@ -1,29 +1,51 @@ -use crate::syntax::{CppName, Namespace, Pair, ResolvableName, Symbol, Types}; +use crate::syntax::{Namespace, Pair, ResolvableName, Symbol, Types}; use proc_macro2::{Ident, Span}; +use std::iter; use syn::Token; impl Pair { - /// Use this constructor when the item can't have a different - /// name in Rust and C++. + // Use this constructor when the item can't have a different name in Rust + // and C++. pub fn new(namespace: Namespace, ident: Ident) -> Self { Self { - rust: ident.clone(), - cxx: CppName::new(namespace, ident), + namespace, + cxx: ident.clone(), + rust: ident, } } - /// Use this constructor when attributes such as #[rust_name] - /// can be used to potentially give a different name in Rust vs C++. + // Use this constructor when attributes such as #[rust_name] can be used to + // potentially give a different name in Rust vs C++. pub fn new_from_differing_names( namespace: Namespace, cxx_ident: Ident, rust_ident: Ident, ) -> Self { Self { + namespace, + cxx: cxx_ident, rust: rust_ident, - cxx: CppName::new(namespace, cxx_ident), } } + + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) + } + + fn iter_all_segments(&self) -> impl Iterator { + self.namespace.iter().chain(iter::once(&self.cxx)) + } + + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } } impl ResolvableName { @@ -49,28 +71,3 @@ impl ResolvableName { types.resolve(self).to_symbol() } } - -impl CppName { - pub fn new(namespace: Namespace, ident: Ident) -> Self { - Self { namespace, ident } - } - - fn iter_all_segments(&self) -> impl Iterator { - self.namespace.iter().chain(std::iter::once(&self.ident)) - } - - fn join(&self, sep: &str) -> String { - self.iter_all_segments() - .map(|s| s.to_string()) - .collect::>() - .join(sep) - } - - pub fn to_symbol(&self) -> Symbol { - Symbol::from_idents(self.iter_all_segments()) - } - - pub fn to_fully_qualified(&self) -> String { - format!("::{}", self.join("::")) - } -} diff --git a/syntax/symbol.rs b/syntax/symbol.rs index c849a62..253f57d 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,5 +1,5 @@ use crate::syntax::namespace::Namespace; -use crate::syntax::CppName; +use crate::syntax::Pair; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -79,10 +79,10 @@ impl Segment for Namespace { } } -impl Segment for CppName { +impl Segment for Pair { fn write(&self, symbol: &mut Symbol) { self.namespace.write(symbol); - self.ident.write(symbol); + self.cxx.write(symbol); } } diff --git a/syntax/types.rs b/syntax/types.rs index 178da3e..d9d6361 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -2,8 +2,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; use crate::syntax::{ - Api, CppName, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type, - TypeAlias, + Api, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type, TypeAlias, }; use proc_macro2::Ident; use quote::ToTokens; @@ -19,7 +18,7 @@ pub struct Types<'a> { pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, pub explicit_impls: Set<&'a Impl>, - pub resolutions: Map<&'a Ident, &'a CppName>, + pub resolutions: Map<&'a Ident, &'a Pair>, } impl<'a> Types<'a> { @@ -56,7 +55,7 @@ impl<'a> Types<'a> { } let mut add_resolution = |pair: &'a Pair| { - resolutions.insert(&pair.rust, &pair.cxx); + resolutions.insert(&pair.rust, pair); }; let mut type_names = UnorderedSet::new(); @@ -228,7 +227,7 @@ impl<'a> Types<'a> { false } - pub fn resolve(&self, ident: &ResolvableName) -> &CppName { + pub fn resolve(&self, ident: &ResolvableName) -> &Pair { self.resolutions .get(&ident.rust) .expect("Unable to resolve type") From da71ef6aa5767defe09a2cbda218341fcb7f8f3c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:36:03 +0000 Subject: [PATCH 1225/2232] Remove ResolvableName from Ty1 This is not treated as resolvable for now. It only supports a hardcoded set of generic types (UniquePtr, CxxVector, Box, Vec). --- diff --git a/syntax/mod.rs b/syntax/mod.rs index 6684b6a..782d617 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -160,7 +160,7 @@ pub enum Type { } pub struct Ty1 { - pub name: ResolvableName, + pub name: Ident, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], diff --git a/syntax/parse.rs b/syntax/parse.rs index c170f79..6b5d925 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -602,15 +602,14 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result { if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); - let maybe_resolved_ident = ResolvableName::new(ident.clone()); match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(maybe_resolved_ident)), + PathArguments::None => return Ok(Type::Ident(ResolvableName::new(ident))), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg, namespace)?; return Ok(Type::UniquePtr(Box::new(Ty1 { - name: maybe_resolved_ident, + name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -620,7 +619,7 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg, namespace)?; return Ok(Type::CxxVector(Box::new(Ty1 { - name: maybe_resolved_ident, + name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -630,7 +629,7 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg, namespace)?; return Ok(Type::RustBox(Box::new(Ty1 { - name: maybe_resolved_ident, + name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -640,7 +639,7 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result { if let GenericArgument::Type(arg) = &generic.args[0] { let inner = parse_type(arg, namespace)?; return Ok(Type::RustVec(Box::new(Ty1 { - name: maybe_resolved_ident, + name: ident, langle: generic.lt_token, inner, rangle: generic.gt_token, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 57db8eb..2b32532 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -39,7 +39,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.name.span(); - let name = self.name.rust.to_string(); + let name = self.name.to_string(); if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); } else if name == "Vec" { From 17a934c4040c30eaa6e3f81a937301d51d7378bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:53:59 +0000 Subject: [PATCH 1226/2232] Rename non-Ident fields previously named ident It will be clearer to avoid using 'ident' to refer to anything but Ident. --- diff --git a/gen/src/namespace.rs b/gen/src/namespace.rs index 7343875..b79c38f 100644 --- a/gen/src/namespace.rs +++ b/gen/src/namespace.rs @@ -4,10 +4,10 @@ use crate::syntax::Api; impl Api { pub fn namespace(&self) -> &Namespace { match self { - Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.ident.namespace, - Api::CxxType(ety) | Api::RustType(ety) => &ety.ident.namespace, - Api::Enum(enm) => &enm.ident.namespace, - Api::Struct(strct) => &strct.ident.namespace, + Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace, + Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace, + Api::Enum(enm) => &enm.name.namespace, + Api::Struct(strct) => &strct.name.namespace, Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), } } diff --git a/gen/src/nested.rs b/gen/src/nested.rs index 0a8816c..22b0c9f 100644 --- a/gen/src/nested.rs +++ b/gen/src/nested.rs @@ -117,7 +117,7 @@ mod tests { fn assert_ident(api: &Api, expected: &str) { if let Api::CxxType(cxx_type) = api { - assert_eq!(cxx_type.ident.cxx.ident, expected); + assert_eq!(cxx_type.name.cxx, expected); } else { unreachable!() } @@ -128,7 +128,7 @@ mod tests { Api::CxxType(ExternType { doc: Doc::new(), type_token: Token![type](Span::call_site()), - ident: Pair::new(ns, Ident::new(ident, Span::call_site())), + name: Pair::new(ns, Ident::new(ident, Span::call_site())), semi_token: Token![;](Span::call_site()), trusted: false, }) diff --git a/gen/src/write.rs b/gen/src/write.rs index 7c16e81..8688797 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -32,7 +32,7 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec true, - Api::Enum(enm) => !out.types.cxx.contains(&enm.ident.rust), + Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust), _ => false, }; @@ -47,10 +47,10 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { for api in apis { write!(out, "{:1$}", "", indent); match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx), + Api::Struct(strct) => write_struct_decl(out, &strct.name.cxx), Api::Enum(enm) => write_enum_decl(out, enm), - Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx), + Api::CxxType(ety) => write_struct_using(out, &ety.name), + Api::RustType(ety) => write_struct_decl(out, &ety.name.cxx), _ => unreachable!(), } } @@ -80,20 +80,20 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { match api { Api::Struct(strct) => { out.next_section(); - if !out.types.cxx.contains(&strct.ident.rust) { + if !out.types.cxx.contains(&strct.name.rust) { write_struct(out, strct); } } Api::Enum(enm) => { out.next_section(); - if out.types.cxx.contains(&enm.ident.rust) { + if out.types.cxx.contains(&enm.name.rust) { check_enum(out, enm); } else { write_enum(out, enm); } } Api::RustType(ety) => { - if let Some(methods) = methods_for_type.get(&ety.ident.rust) { + if let Some(methods) = methods_for_type.get(&ety.name.rust) { out.next_section(); write_struct_with_methods(out, ety, methods); } @@ -105,8 +105,8 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { out.next_section(); for api in apis { if let Api::TypeAlias(ety) = api { - if out.types.required_trivial.contains_key(&ety.ident.rust) { - check_trivial_extern_type(out, &ety.ident) + if out.types.required_trivial.contains_key(&ety.name.rust) { + check_trivial_extern_type(out, &ety.name) } } } @@ -193,14 +193,14 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { } fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { - out.set_namespace(&strct.ident.namespace); - let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_symbol()); + out.set_namespace(&strct.name.namespace); + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.name.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident.cxx); + writeln!(out, "struct {} final {{", strct.name.cxx); for field in &strct.fields { write!(out, " "); write_type_space(out, &field.ty); @@ -215,7 +215,7 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { } fn write_enum_decl(out: &mut OutFile, enm: &Enum) { - write!(out, "enum class {} : ", enm.ident.cxx); + write!(out, "enum class {} : ", enm.name.cxx); write_atom(out, enm.repr); writeln!(out, ";"); } @@ -229,24 +229,24 @@ fn write_struct_with_methods<'a>( ety: &'a ExternType, methods: &[&ExternFn], ) { - out.set_namespace(&ety.ident.namespace); - let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_symbol()); + out.set_namespace(&ety.name.namespace); + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.name.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident.cxx); - writeln!(out, " {}() = delete;", ety.ident.cxx); + writeln!(out, "struct {} final {{", ety.name.cxx); + writeln!(out, " {}() = delete;", ety.name.cxx); writeln!( out, " {}(const {} &) = delete;", - ety.ident.cxx, ety.ident.cxx, + ety.name.cxx, ety.name.cxx, ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.to_string(); + let local_name = method.name.cxx.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -255,14 +255,14 @@ fn write_struct_with_methods<'a>( } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { - out.set_namespace(&enm.ident.namespace); - let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_symbol()); + out.set_namespace(&enm.name.namespace); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.name.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident.cxx); + write!(out, "enum class {} : ", enm.name.cxx); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -273,8 +273,8 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { - out.set_namespace(&enm.ident.namespace); - write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident.cxx); + out.set_namespace(&enm.name.namespace); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -283,7 +283,7 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident.cxx, variant.ident, variant.discriminant, + enm.name.cxx, variant.ident, variant.discriminant, ); } } @@ -331,7 +331,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) { fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.next_section(); - out.set_namespace(&efn.ident.namespace); + out.set_namespace(&efn.name.namespace); out.begin_block(Block::ExternC); if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); @@ -377,12 +377,12 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write!(out, " "); write_return_type(out, &efn.ret); match &efn.receiver { - None => write!(out, "(*{}$)(", efn.ident.rust), + None => write!(out, "(*{}$)(", efn.name.rust), Some(receiver) => write!( out, "({}::*{}$)(", out.types.resolve(&receiver.ty).to_fully_qualified(), - efn.ident.rust, + efn.name.rust, ), } for (i, arg) in efn.args.iter().enumerate() { @@ -399,12 +399,12 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.to_fully_qualified()), + None => write!(out, "{}", efn.name.to_fully_qualified()), Some(receiver) => write!( out, "&{}::{}", out.types.resolve(&receiver.ty).to_fully_qualified(), - efn.ident.cxx, + efn.name.cxx, ), } writeln!(out, ";"); @@ -438,8 +438,8 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { _ => {} } match &efn.receiver { - None => write!(out, "{}$(", efn.ident.rust), - Some(_) => write!(out, "(self.*{}$)(", efn.ident.rust), + None => write!(out, "{}$(", efn.name.rust), + Some(_) => write!(out, "(self.*{}$)(", efn.name.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -533,7 +533,7 @@ fn write_function_pointer_trampoline( } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { - out.set_namespace(&efn.ident.namespace); + out.set_namespace(&efn.name.namespace); out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; @@ -592,13 +592,13 @@ fn write_rust_function_decl_impl( } fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { - out.set_namespace(&efn.ident.namespace); + out.set_namespace(&efn.name.namespace); for line in efn.doc.to_string().lines() { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.to_string(), - Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.ident.cxx), + None => efn.name.cxx.to_string(), + Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.name.cxx), }; let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 026ec43..029c54f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -43,10 +43,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { - let ident = &ety.ident; - if !types.structs.contains_key(&ident.rust) - && !types.enums.contains_key(&ident.rust) - { + let ident = &ety.name.rust; + if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { expanded.extend(expand_cxx_type(ety)); } } @@ -125,10 +123,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } fn expand_struct(strct: &Struct) -> TokenStream { - let ident = &strct.ident; + let ident = &strct.name.rust; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(&strct.ident); + let type_id = type_id(&strct.name); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -152,10 +150,10 @@ fn expand_struct(strct: &Struct) -> TokenStream { } fn expand_enum(enm: &Enum) -> TokenStream { - let ident = &enm.ident; + let ident = &enm.name.rust; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(&enm.ident); + let type_id = type_id(&enm.name); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -185,9 +183,9 @@ fn expand_enum(enm: &Enum) -> TokenStream { } fn expand_cxx_type(ety: &ExternType) -> TokenStream { - let ident = &ety.ident; + let ident = &ety.name.rust; let doc = &ety.doc; - let type_id = type_id(&ety.ident); + let type_id = type_id(&ety.name); quote! { #doc @@ -235,7 +233,7 @@ fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { outparam = Some(quote!(__return: *mut #ret)); } let link_name = mangle::extern_fn(efn, types); - let local_name = format_ident!("__{}", efn.ident.rust); + let local_name = format_ident!("__{}", efn.name.rust); quote! { #[link_name = #link_name] fn #local_name(#(#all_args,)* #outparam) #ret; @@ -323,7 +321,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { } }) .collect::(); - let local_name = format_ident!("__{}", efn.ident.rust); + let local_name = format_ident!("__{}", efn.name.rust); let call = if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap()); setup.extend(quote! { @@ -420,7 +418,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { if unsafety.is_none() { dispatch = quote!(unsafe { #dispatch }); } - let ident = &efn.ident; + let ident = &efn.name.rust; let function_shim = quote! { #doc pub #unsafety fn #ident(#(#all_args,)*) #ret { @@ -449,7 +447,7 @@ fn expand_function_pointer_trampoline( let c_trampoline = mangle::c_trampoline(efn, var, types); let r_trampoline = mangle::r_trampoline(efn, var, types); let local_name = parse_quote!(__); - let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); + let catch_unwind_label = format!("::{}::{}", efn.name.rust, var); let shim = expand_rust_function_shim_impl( sig, types, @@ -475,7 +473,7 @@ fn expand_function_pointer_trampoline( } fn expand_rust_type(ety: &ExternType) -> TokenStream { - let ident = &ety.ident; + let ident = &ety.name.rust; quote! { use super::#ident; } @@ -490,12 +488,12 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { // | doesn't have a size known at compile-time // required by this bound in `ffi::_::__AssertSized` - let ident = &ety.ident; + let ident = &ety.name.rust; let begin_span = Token![::](ety.type_token.span); let sized = quote_spanned! {ety.semi_token.span=> #begin_span std::marker::Sized }; - quote_spanned! {ident.rust.span()=> + quote_spanned! {ident.span()=> let _ = { fn __AssertSized() {} __AssertSized::<#ident> @@ -505,9 +503,9 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let link_name = mangle::extern_fn(efn, types); - let local_name = format_ident!("__{}", efn.ident.rust); - let catch_unwind_label = format!("::{}", efn.ident.rust); - let invoke = Some(&efn.ident.rust); + let local_name = format_ident!("__{}", efn.name.rust); + let catch_unwind_label = format!("::{}", efn.name.rust); + let invoke = Some(&efn.name.rust); expand_rust_function_shim_impl( efn, types, @@ -675,7 +673,7 @@ fn expand_rust_function_shim_impl( fn expand_type_alias(alias: &TypeAlias) -> TokenStream { let doc = &alias.doc; - let ident = &alias.ident; + let ident = &alias.name.rust; let ty = &alias.ty; quote! { #doc @@ -684,8 +682,8 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { - let ident = &alias.ident; - let type_id = type_id(ident); + let ident = &alias.name.rust; + let type_id = type_id(&alias.name); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); @@ -695,7 +693,7 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { const _: fn() = #begin #ident, #type_id #end; }; - if types.required_trivial.contains_key(&alias.ident.rust) { + if types.required_trivial.contains_key(&alias.name.rust) { let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<); verify.extend(quote! { const _: fn() = #begin #ident, ::cxx::kind::Trivial #end; diff --git a/syntax/check.rs b/syntax/check.rs index ba488e1..e8a6448 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -167,16 +167,16 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) { } fn check_api_struct(cx: &mut Check, strct: &Struct) { - let ident = &strct.ident; - check_reserved_name(cx, &ident.rust); + let name = &strct.name; + check_reserved_name(cx, &name.rust); 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(&ident.rust) { - if let Some(ety) = cx.types.untrusted.get(&ident.rust) { + 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); } @@ -198,7 +198,7 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { } fn check_api_enum(cx: &mut Check, enm: &Enum) { - check_reserved_name(cx, &enm.ident.rust); + check_reserved_name(cx, &enm.name.rust); if enm.variants.is_empty() { let span = span_for_enum_error(enm); @@ -207,13 +207,13 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { } fn check_api_type(cx: &mut Check, ety: &ExternType) { - check_reserved_name(cx, &ety.ident.rust); + check_reserved_name(cx, &ety.name.rust); - if let Some(reason) = cx.types.required_trivial.get(&ety.ident.rust) { + if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) { let what = match reason { - TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident.rust), - TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), - TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), + TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust), + TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust), + TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust), }; let msg = format!( "needs a cxx::ExternType impl in order to be used as {}", diff --git a/syntax/ident.rs b/syntax/ident.rs index bb2fe46..aaf7832 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -24,28 +24,28 @@ pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check_ident(cx, &strct.ident); + check_ident(cx, &strct.name); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check_ident(cx, &enm.ident); + check_ident(cx, &enm.name); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check_ident(cx, &ety.ident); + check_ident(cx, &ety.name); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { - check(cx, &efn.ident.rust); + check(cx, &efn.name.rust); for arg in &efn.args { check(cx, &arg.ident); } } Api::TypeAlias(alias) => { - check_ident(cx, &alias.ident); + check_ident(cx, &alias.name); } } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 55ffca5..cc5115a 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -15,13 +15,13 @@ pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { Some(receiver) => { let receiver_ident = types.resolve(&receiver.ty); join!( - efn.ident.namespace, + efn.name.namespace, CXXBRIDGE, receiver_ident.cxx, - efn.ident.rust + efn.name.rust ) } - None => join!(efn.ident.namespace, CXXBRIDGE, efn.ident.rust), + None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 782d617..ac6350a 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -67,7 +67,7 @@ pub enum IncludeKind { pub struct ExternType { pub doc: Doc, pub type_token: Token![type], - pub ident: Pair, + pub name: Pair, pub semi_token: Token![;], pub trusted: bool, } @@ -76,7 +76,7 @@ pub struct Struct { pub doc: Doc, pub derives: Vec, pub struct_token: Token![struct], - pub ident: Pair, + pub name: Pair, pub brace_token: Brace, pub fields: Vec, } @@ -84,7 +84,7 @@ pub struct Struct { pub struct Enum { pub doc: Doc, pub enum_token: Token![enum], - pub ident: Pair, + pub name: Pair, pub brace_token: Brace, pub variants: Vec, pub repr: Atom, @@ -93,7 +93,7 @@ pub struct Enum { pub struct ExternFn { pub lang: Lang, pub doc: Doc, - pub ident: Pair, + pub name: Pair, pub sig: Signature, pub semi_token: Token![;], } @@ -101,7 +101,7 @@ pub struct ExternFn { pub struct TypeAlias { pub doc: Doc, pub type_token: Token![type], - pub ident: Pair, + pub name: Pair, pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], diff --git a/syntax/parse.rs b/syntax/parse.rs index 6b5d925..2136653 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -90,7 +90,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct, mut namespace: Namespace) -> doc, derives, struct_token: item.struct_token, - ident: Pair::new(namespace.clone(), item.ident), + name: Pair::new(namespace.clone(), item.ident), brace_token: fields.brace_token, fields: fields .named @@ -177,7 +177,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, mut namespace: Namespace) -> Resu Ok(Api::Enum(Enum { doc, enum_token, - ident: Pair::new(namespace, item.ident), + name: Pair::new(namespace, item.ident), brace_token, variants, repr, @@ -241,8 +241,8 @@ fn parse_foreign_mod( } let mut types = items.iter().filter_map(|item| match item { - Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.ident), - Api::TypeAlias(alias) => Some(&alias.ident), + Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name), + Api::TypeAlias(alias) => Some(&alias.name), _ => None, }); if let (Some(single_type), None) = (types.next(), types.next()) { @@ -305,7 +305,7 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident: Pair::new(namespace, ident), + name: Pair::new(namespace, ident), semi_token, trusted, })) @@ -404,7 +404,7 @@ fn parse_extern_fn( let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; - let ident = Pair::new_from_differing_names( + let name = Pair::new_from_differing_names( namespace, cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), rust_name.unwrap_or(foreign_fn.sig.ident.clone()), @@ -419,7 +419,7 @@ fn parse_extern_fn( Ok(api_function(ExternFn { lang, doc, - ident, + name, sig: Signature { unsafety, fn_token, @@ -468,7 +468,7 @@ fn parse_extern_verbatim( Ok(TypeAlias { doc, type_token, - ident: Pair::new(namespace, ident), + name: Pair::new(namespace, ident), eq_token, ty, semi_token, diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 2b32532..15c23dc 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -85,7 +85,7 @@ 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.ident.to_tokens(tokens); + self.name.to_tokens(tokens); } } @@ -93,7 +93,7 @@ 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.ident.to_tokens(tokens); + self.name.to_tokens(tokens); } } @@ -101,7 +101,7 @@ 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.ident.to_tokens(tokens); + self.name.to_tokens(tokens); } } @@ -109,7 +109,7 @@ 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.ident.to_tokens(tokens); + self.name.to_tokens(tokens); } } diff --git a/syntax/types.rs b/syntax/types.rs index d9d6361..67b041b 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -70,7 +70,7 @@ impl<'a> Types<'a> { match api { Api::Include(_) => {} Api::Struct(strct) => { - let ident = &strct.ident.rust; + let ident = &strct.name.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -81,14 +81,14 @@ impl<'a> Types<'a> { // type, then error. duplicate_name(cx, strct, ident); } - structs.insert(&strct.ident.rust, strct); + structs.insert(&strct.name.rust, strct); for field in &strct.fields { visit(&mut all, &field.ty); } - add_resolution(&strct.ident); + add_resolution(&strct.name); } Api::Enum(enm) => { - let ident = &enm.ident.rust; + let ident = &enm.name.rust; if !type_names.insert(ident) && (!cxx.contains(ident) || structs.contains_key(ident) @@ -100,10 +100,10 @@ impl<'a> Types<'a> { duplicate_name(cx, enm, ident); } enums.insert(ident, enm); - add_resolution(&enm.ident); + add_resolution(&enm.name); } Api::CxxType(ety) => { - let ident = &ety.ident.rust; + let ident = &ety.name.rust; if !type_names.insert(ident) && (cxx.contains(ident) || !structs.contains_key(ident) && !enums.contains_key(ident)) @@ -117,21 +117,21 @@ impl<'a> Types<'a> { if !ety.trusted { untrusted.insert(ident, ety); } - add_resolution(&ety.ident); + add_resolution(&ety.name); } Api::RustType(ety) => { - let ident = &ety.ident.rust; + let ident = &ety.name.rust; if !type_names.insert(ident) { duplicate_name(cx, ety, ident); } rust.insert(ident); - add_resolution(&ety.ident); + add_resolution(&ety.name); } 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.ident.rust)) { - duplicate_name(cx, efn, &efn.ident.rust); + 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); @@ -141,13 +141,13 @@ impl<'a> Types<'a> { } } Api::TypeAlias(alias) => { - let ident = &alias.ident; - if !type_names.insert(&ident.rust) { - duplicate_name(cx, alias, &ident.rust); + let ident = &alias.name.rust; + if !type_names.insert(ident) { + duplicate_name(cx, alias, ident); } - cxx.insert(&ident.rust); - aliases.insert(&ident.rust, alias); - add_resolution(&alias.ident); + cxx.insert(ident); + aliases.insert(ident, alias); + add_resolution(&alias.name); } Api::Impl(imp) => { visit(&mut all, &imp.ty); From 9bb232b12e73722c896ee05fe8e75452d0d5b89f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 08:55:51 +0000 Subject: [PATCH 1227/2232] Remove ToTokens for Pair Clearer to be explicit at the call sites. --- diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 15c23dc..500ea0b 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,6 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Atom, Derive, Enum, ExternFn, ExternType, Impl, Pair, Receiver, Ref, ResolvableName, Signature, + Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; @@ -85,7 +85,7 @@ 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.to_tokens(tokens); + self.name.rust.to_tokens(tokens); } } @@ -93,7 +93,7 @@ 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.to_tokens(tokens); + self.name.rust.to_tokens(tokens); } } @@ -101,7 +101,7 @@ 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.to_tokens(tokens); + self.name.rust.to_tokens(tokens); } } @@ -109,7 +109,7 @@ 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.to_tokens(tokens); + self.name.rust.to_tokens(tokens); } } @@ -121,12 +121,6 @@ impl ToTokens for ExternFn { } } -impl ToTokens for Pair { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.rust.to_tokens(tokens); - } -} - impl ToTokens for Impl { fn to_tokens(&self, tokens: &mut TokenStream) { self.impl_token.to_tokens(tokens); From dcfa8e91c6ce09f8ddd78968518d88d2629d0529 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 17:57:45 +0000 Subject: [PATCH 1228/2232] Move include information of builtins to builtin.rs --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index 36e32f8..ab2b465 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -41,6 +41,38 @@ pub(super) fn write(out: &mut OutFile) { let builtin = &mut out.builtin; let out = &mut builtin.content; + if builtin.rust_string { + include.array = true; + include.cstdint = true; + include.string = true; + } + + if builtin.rust_str { + include.cstdint = true; + include.string = true; + } + + if builtin.rust_box { + include.new = true; + include.type_traits = true; + } + + if builtin.rust_vec { + include.array = true; + include.new = true; + include.type_traits = true; + builtin.panic = true; + builtin.unsafe_bitcopy = true; + } + + if builtin.rust_error { + include.exception = true; + } + + if builtin.rust_isize { + include.basetsd = true; + } + out.begin_block(Block::Namespace("rust")); out.begin_block(Block::InlineNamespace("cxxbridge05")); writeln!(out, "// #include \"rust/cxx.h\""); diff --git a/gen/src/write.rs b/gen/src/write.rs index 8688797..2c3234c 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -144,45 +144,18 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, - Some(Isize) => { - out.include.basetsd = true; - out.builtin.rust_isize = true; - } + Some(Isize) => out.builtin.rust_isize = true, Some(CxxString) => out.include.string = true, - Some(RustString) => { - out.include.array = true; - out.include.cstdint = true; - out.include.string = true; - out.builtin.rust_string = true; - } + Some(RustString) => out.builtin.rust_string = true, Some(Bool) | Some(F32) | Some(F64) | None => {} }, - Type::RustBox(_) => { - out.include.new = true; - out.include.type_traits = true; - out.builtin.rust_box = true; - } - Type::RustVec(_) => { - out.include.array = true; - out.include.new = true; - out.include.type_traits = true; - out.builtin.panic = true; - out.builtin.rust_vec = true; - out.builtin.unsafe_bitcopy = true; - } + Type::RustBox(_) => out.builtin.rust_box = true, + Type::RustVec(_) => out.builtin.rust_vec = true, Type::UniquePtr(_) => out.include.memory = true, - Type::Str(_) => { - out.include.cstdint = true; - out.include.string = true; - out.builtin.rust_str = true; - } + Type::Str(_) => out.builtin.rust_str = true, Type::CxxVector(_) => out.include.vector = true, - Type::Fn(_) => { - out.builtin.rust_fn = true; - } - Type::Slice(_) => { - out.builtin.rust_slice = true; - } + Type::Fn(_) => out.builtin.rust_fn = true, + Type::Slice(_) => out.builtin.rust_slice = true, Type::SliceRefU8(_) => { out.include.cstdint = true; out.builtin.rust_slice = true; @@ -750,7 +723,6 @@ fn write_rust_function_shim_impl( } writeln!(out, ";"); if sig.throws { - out.include.exception = true; out.builtin.rust_error = true; writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); From 174bd951168e0c72d5b612487c70717e135bc20f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Nov 02 2020 18:23:52 +0000 Subject: [PATCH 1229/2232] Expose way to bypass trivial destr/move on extern types passed by value --- diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index ab2b465..7a4ddcd 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -23,6 +23,7 @@ pub struct Builtins<'a> { pub rust_slice_new: bool, pub rust_slice_repr: bool, pub exception: bool, + pub relocatable: bool, pub content: Content<'a>, } @@ -73,6 +74,10 @@ pub(super) fn write(out: &mut OutFile) { include.basetsd = true; } + if builtin.relocatable { + include.type_traits = true; + } + out.begin_block(Block::Namespace("rust")); out.begin_block(Block::InlineNamespace("cxxbridge05")); writeln!(out, "// #include \"rust/cxx.h\""); @@ -100,6 +105,7 @@ pub(super) fn write(out: &mut OutFile) { ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN"); ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR"); ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE"); + ifndef::write(out, builtin.relocatable, "CXXBRIDGE05_RELOCATABLE"); if builtin.manually_drop { out.next_section(); diff --git a/gen/src/write.rs b/gen/src/write.rs index 2c3234c..c806e55 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -262,7 +262,7 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) { - // NOTE: The following two static assertions are just nice-to-have and not + // NOTE: The following static assertion is just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: // @@ -273,31 +273,35 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) { // // Since the user went on the record with their unsafe impl to unsafely // claim they KNOW that the type is trivial, it's fine for that to be on - // them if that were wrong. + // them if that were wrong. However, in practice correctly reasoning about + // the relocatability of C++ types is challenging, particularly if the type + // definition were to change over time, so for now we add this check. + // + // There may be legitimate reasons to opt out of this assertion for support + // of types that the programmer knows are soundly Rust-movable despite not + // being recognized as such by the C++ type system due to a move constructor + // or destructor. To opt out of the relocatability check, they need to do + // one of the following things in any header used by `include!` in their + // bridge. + // + // --- if they define the type: + // struct MyType { + // ... + // + using IsRelocatable = std::true_type; + // }; + // + // --- otherwise: + // + template <> + // + struct rust::IsRelocatable : std::true_type {}; // - // There may be a legitimate reason we'll want to remove these assertions - // for support of types that the programmer knows are Rust-movable despite - // not being recognized as such by the C++ type system due to a move - // constructor or destructor. - let id = &id.to_fully_qualified(); - out.include.type_traits = true; - writeln!(out, "static_assert("); - writeln!( - out, - " ::std::is_trivially_move_constructible<{}>::value,", - id, - ); - writeln!( - out, - " \"type {} marked as Trivial in Rust is not trivially move constructible in C++\");", - id, - ); + let id = id.to_fully_qualified(); + out.builtin.relocatable = true; writeln!(out, "static_assert("); - writeln!(out, " ::std::is_trivially_destructible<{}>::value,", id); + writeln!(out, " ::rust::IsRelocatable<{}>::value,", id); writeln!( out, - " \"type {} marked as Trivial in Rust is not trivially destructible in C++\");", + " \"type {} marked as Trivial in Rust is not trivially move constructible and trivially destructible in C++\");", id, ); } diff --git a/include/cxx.h b/include/cxx.h index 45c3828..b048e27 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -267,6 +267,27 @@ using isize = ssize_t; std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); +// IsRelocatable is used in assertions that a C++ type passed by value +// between Rust and C++ is soundly relocatable by Rust. +// +// There may be legitimate reasons to opt out of the check for support of types +// that the programmer knows are soundly Rust-movable despite not being +// recognized as such by the C++ type system due to a move constructor or +// destructor. To opt out of the relocatability check, do either of the +// following things in any header used by `include!` in the bridge. +// +// --- if you define the type: +// struct MyType { +// ... +// + using IsRelocatable = std::true_type; +// }; +// +// --- otherwise: +// + template <> +// + struct rust::IsRelocatable : std::true_type {}; +template +struct IsRelocatable; + // Snake case aliases for use in code that uses this style for type names. using string = String; using str = Str; @@ -281,6 +302,8 @@ template using fn = Fn; template using try_fn = TryFn; +template +using is_relocatable = IsRelocatable; @@ -552,5 +575,42 @@ template Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} #endif // CXXBRIDGE05_RUST_VEC +#ifndef CXXBRIDGE05_RELOCATABLE +#define CXXBRIDGE05_RELOCATABLE +namespace detail { +template +struct make_void { + using type = void; +}; + +template +using void_t = typename make_void::type; + +template class, typename...> +struct detect : std::false_type {}; +template