cxx icon indicating copy to clipboard operation
cxx copied to clipboard

Design for panic-via-exception handling (and vice versa)

Open schreter opened this issue 1 year ago • 3 comments

In our project which requires perfect C++/Rust integration regarding error handling we extended cxx crate to handle panics as C++ exceptions as well. This works well, however, the panic is only handled whenever the Rust function returns a Result (as otherwise C++ exception is not generated at all). The question is, how can we handle panics also in non-Result functions returning only the value.

There are following possibilities:

  • Do not handle it at all, i.e., keep status quo and only handle it in Result functions (that's the current state of implementation, which I can push upstream).
  • Handle panics in general for all Rust functions and throw them as a RustPanic exception in C++. This requires the change of the generated code on the C++ side.

Of course, we prefer the latter, since that would transparently handle all Rust errors and propagate them to C++ without crashing the program because of stumbling over the ABI boundary (and transparent C++/Rust integration is the goal of cxx crate 🙂).

However, since in principle any Rust function can panic, implementing this requires indirect return for all values. Considering that the bridge is relatively "heavyweight" anyway, this should be no big issue, though. Aside from that, cross-language LTO will remove unnecessary moves, effectively producing (almost) optimal code.

The inverse issue, handling C++ exceptions via panics in non-Result C++ functions is the same in green - again, it would entail an indirect return and handling the exception by turning it into a panic in the Rust part of the bridge. We can even transparently pass the C++ exception through Rust frames with this, so outer C++ calling Rust calling inner C++ throwing exception will land in the outer C++ with exactly the same exception thrown in the inner C++ code (there is already a pending change https://github.com/dtolnay/cxx/pull/1180 to handle C++ exceptions transparently, so we can build on top of it).

Therefore, are there any objections against changing the generator to always handle the return value via an indirection, so we can reliably turn panics to C++ exceptions and vice versa (possibly configurable on bridge level)? If so, what is the alternative? If not, I'd go with the implementation in that direction.

Alternatively, does anyone have a better idea how to handle it?

Any other ideas/proposals/wishes regarding this topic?

Thanks.

schreter avatar Feb 26 '23 17:02 schreter

I would be on board with turning all Rust panicks to C++ exceptions (throw rust::Panic) and C++ exceptions to Rust panicks.

dtolnay avatar Feb 26 '23 17:02 dtolnay

@dtolnay

I would be on board with turning all Rust panicks to C++ exceptions (throw rust::Panic) and C++ exceptions to Rust panicks.

Perfect. But just to reiterate, this also means that we need to return values via indirect return in all cases, which might be potential performance regression (though my hope is that LTO will fix that). Is that fine from your POV? Thanks.

schreter avatar Feb 26 '23 18:02 schreter

That's fine.

dtolnay avatar Feb 27 '23 00:02 dtolnay