Skip to content

Commit

Permalink
Merge pull request #73 from dtolnay/result
Browse files Browse the repository at this point in the history
Implement conversion of Result⟶exception
  • Loading branch information
dtolnay authored Mar 17, 2020
2 parents 277e3cc + 486b6ec commit 8c1a558
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 19 deletions.
21 changes: 18 additions & 3 deletions gen/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);");
}
Expand All @@ -391,7 +406,7 @@ fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
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<Type>, types: &Types) {
Expand Down
13 changes: 13 additions & 0 deletions include/cxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,26 @@ template <typename T> class Box final {
};
#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 &);

// Snake case aliases for use in code that uses this style for type names.
using string = String;
using str = Str;
template <class T> using box = Box<T>;
using error = Error;

struct unsafe_bitcopy_t {
explicit unsafe_bitcopy_t() = default;
Expand Down
29 changes: 19 additions & 10 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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()
Expand All @@ -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);
Expand Down Expand Up @@ -407,9 +415,10 @@ fn expand_return_type(ret: &Option<Type>) -> TokenStream {
}
}

fn indirect_return(ret: &Option<Type>, 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 {
Expand Down
26 changes: 26 additions & 0 deletions src/cxx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ mod error;
mod gen;
mod opaque;
mod paths;
mod result;
mod rust_str;
mod rust_string;
mod syntax;
Expand All @@ -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;
Expand Down
43 changes: 43 additions & 0 deletions src/result.rs
Original file line number Diff line number Diff line change
@@ -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<T, E>(ret: *mut T, result: StdResult<T, E>) -> 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 }
}
6 changes: 0 additions & 6 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
));
}
}
_ => {}
}
Expand Down
23 changes: 23 additions & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cxx::{CxxString, UniquePtr};
use std::fmt::{self, Display};

#[cxx::bridge(namespace = tests)]
pub mod ffi {
Expand Down Expand Up @@ -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<CxxString>);

fn r_try_return_primitive() -> Result<usize>;
fn r_fail_return_primitive() -> Result<usize>;
}
}

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
}
Expand Down Expand Up @@ -131,3 +146,11 @@ fn r_take_rust_string(s: String) {
fn r_take_unique_ptr_string(s: UniquePtr<CxxString>) {
assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
}

fn r_try_return_primitive() -> Result<usize, Error> {
Ok(2020)
}

fn r_fail_return_primitive() -> Result<usize, Error> {
Err(Error)
}
9 changes: 9 additions & 0 deletions tests/ffi/tests.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "tests/ffi/tests.h"
#include "tests/ffi/lib.rs"
#include <cstring>

extern "C" void cxx_test_suite_set_correct() noexcept;
extern "C" tests::R *cxx_test_suite_get_box() noexcept;
Expand Down Expand Up @@ -126,6 +127,14 @@ extern "C" const char *cxx_run_test() noexcept {
r_take_unique_ptr_string(
std::unique_ptr<std::string>(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;
}
Expand Down

0 comments on commit 8c1a558

Please sign in to comment.