Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions syntax/mangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// defining characteristics:
// - 2 segments
// - starts with cxxbridge
// TODO: should these also include {CXXVERSION}?
//
// (b) Behavior on a builtin binding without generic parameter.
// pattern: {CXXBRIDGE} $ {TYPE} $ {NAME}
Expand All @@ -15,6 +16,7 @@
// defining characteristics:
// - 3 segments
// - starts with cxxbridge
// TODO: should these also include {CXXVERSION}?
//
// (c) Behavior on a builtin binding with generic parameter.
// pattern: {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME}
Expand All @@ -24,33 +26,35 @@
// defining characteristics:
// - 4+ segments
// - starts with cxxbridge
// TODO: should these also include {CXXVERSION}? (always? or only for
// ones implicitly or explicitly `impl`-ed by the user?)
//
// (d) User-defined extern function.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {NAME}
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {NAME}
// examples:
// - cxxbridge1$new_client
// - org$rust$cxxbridge1$new_client
// - cxxbridge1$v187$new_client
// - org$rust$cxxbridge1$v187$new_client
// defining characteristics:
// - cxxbridge is second from end
// - cxxbridge is third from end
// FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace
//
// (e) User-defined extern member function.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME}
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {TYPE} $ {NAME}
// examples:
// - org$cxxbridge1$Struct$get
// - org$cxxbridge1$v187$Struct$get
// defining characteristics:
// - cxxbridge is third from end
// - cxxbridge is fourth from end
// FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names
//
// (f) Operator overload.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME}
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {TYPE} $ operator $ {NAME}
// examples:
// - org$rust$cxxbridge1$Struct$operator$eq
// - org$rust$cxxbridge1$v187$Struct$operator$eq
// defining characteristics:
// - second segment from end is `operator` (not possible in type or namespace names)
//
// (g) Closure trampoline.
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {CXXVERSION} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
// examples:
// - org$rust$cxxbridge1$Struct$invoke$f$0
// defining characteristics:
Expand Down Expand Up @@ -78,6 +82,11 @@ use crate::syntax::{ExternFn, Pair, Types};

const CXXBRIDGE: &str = "cxxbridge1";

// Ignoring `CARGO_PKG_VERSION_MAJOR` and `...MINOR`, because they don't agree across
// all the crates. For example `gen/lib/Cargo.toml` says `version = "0.7.xxx"`, but
// `macro/Cargo.toml` says `version = "1.0.xxx"`.
const CXXVERSION: &str = concat!("v", env!("CARGO_PKG_VERSION_PATCH"));

macro_rules! join {
($($segment:expr),+ $(,)?) => {
symbol::join(&[$(&$segment),+])
Expand All @@ -91,18 +100,20 @@ pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
join!(
efn.name.namespace,
CXXBRIDGE,
CXXVERSION,
self_type_ident.name.cxx,
efn.name.rust,
)
}
None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
None => join!(efn.name.namespace, CXXBRIDGE, CXXVERSION, efn.name.rust),
}
}

pub(crate) fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
join!(
receiver.namespace,
CXXBRIDGE,
CXXVERSION,
receiver.cxx,
"operator",
operator,
Expand Down
17 changes: 12 additions & 5 deletions tests/cxx_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 cxxbridge1$do_cpp_thing(::rust::Str foo)"));
assert!(output.contains(&format!("void {CXXPREFIX}$do_cpp_thing(::rust::Str foo)")));
}

#[test]
Expand All @@ -28,9 +28,13 @@ 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 cxxbridge1$do_cpp_thing(::rust::Str foo)"));
assert!(output.contains(&format!(
"ANNOTATION void {CXXPREFIX}$do_cpp_thing(::rust::Str foo)"
)));
}

const CXXPREFIX: &'static str = concat!("cxxbridge1$v", env!("CARGO_PKG_VERSION_PATCH"));

const BRIDGE1: &str = r#"
#[cxx::bridge]
mod ffi {
Expand Down Expand Up @@ -66,10 +70,13 @@ fn test_extern_rust_method_on_c_type() {
assert!(!header.contains("rust_method_cpp_receiver"));

// Check that there is a generated C signature bridging to the Rust method.
assert!(implementation
.contains("void cxxbridge1$CppType$rust_method_cpp_receiver(::CppType &self) noexcept;"));
assert!(implementation.contains(&format!(
"void {CXXPREFIX}$CppType$rust_method_cpp_receiver(::CppType &self) noexcept;"
)));

// Check that there is an implementation on the C++ class calling the Rust method.
assert!(implementation.contains("void CppType::rust_method_cpp_receiver() noexcept {"));
assert!(implementation.contains("cxxbridge1$CppType$rust_method_cpp_receiver(*this);"));
assert!(implementation.contains(&format!(
"{CXXPREFIX}$CppType$rust_method_cpp_receiver(*this);"
)));
}