Expose `unsafe` functions to C++, as if they were not `unsafe`.

For example (Rust):

```rust
pub unsafe fn foo() {}
```

becomes (C++):

```c++
void foo();
```

And can be called from C++ as such.

Given the initial use-case targeted by Crubit is manually written interop []

### Status Quo

The idea behind not implementing `unsafe` yet was that, well, we do not want to implement something that will prevent us from adopting a better solution later. We should implement it the right way from the start, and in the meantime, do nothing, so that we do not have to deal with technical debt from making a wrong choice at first.

However, because workarounds exist, this doesn't work as well as described. We are only moving the technical debt down to end users. After all, users can wrap them with a safe function, and then expose that to C++ anyway! See b/254095482#comment8.

We might ask: given that users will call unsafe functions, should we allow it by using `unsafe` functions directly in FFI, or should we allow it via user-defined wrapper functions? "Don't allow it" is not an option![0]

If we pick the status quo, people work around this by marking their functions as safe. These workarounds are permanent: once we add `unsafe` FFI, there is no reliable way to go back and find everywhere that people worked around missing `unsafe` FFI, and retroactively add safety support. And so, ironically, by not supporting `unsafe` in FFI, we guarantee a long-term future with more dangerous FFI -- we won't be able to find everywhere we should be adding `unsafe`. In the short term, these workaround functions are more dangerous (if they are callable from Rust, they're not marked unsafe!), and more difficult to review (using `unsafe` in a "weird" way).

In contrast, if we pick a solution -- any complete solution, really -- that allows people to invoke `unsafe` functions over FFI, then people will directly expose their `unsafe` functions into the FFI, and now it becomes **legible**. We can enumerate every single instance of an unsafe function which is callable from C++, and either fix it when we have a better/newer approach, or mark it with an attribute that allowlists it into a fake-safe C++ API. This has larger direct costs: we are forced to actually do this migration when the time comes, or write that ignore attribute. However, these direct costs are just the result of making the invisible hidden costs, visible -- and forcing us to directly come to terms with the existing unsafe FFI.

So: there are concrete benefits to supporting `unsafe`. This does not block us from later adopting a better form of `unsafe` support (though we will need to mark up existing functions, this is not a huge cost). And it will make any kind of long-term technical debt from `unsafe` easier to spot and fix later.

I believe, therefore, that this is a win. We should support `unsafe`, in the short-medium term. In the long-term, it's a research project for how best to expose `unsafe` to C++ code.

### What do other projects do?

We're in good company -- everyone else I tried also does the same thing as this CL proposes doing for Crubit.

#### cbindgen

`unsafe` is ignored, as in this CL:

```rust
#[no_mangle]
pub unsafe extern "C" fn add(left: usize, right: usize) -> usize {
    left + right
}
```

```c++
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>

extern "C" {

uintptr_t add(uintptr_t left, uintptr_t right);

} // extern "C"
```

#### cxx

`unsafe` is ignored, like in this CL:

```rust
#[cxx::bridge]
mod ffi {
    extern "Rust" {
        unsafe fn add(left: usize, right: usize) -> usize;
    }
}

pub unsafe fn add(left: usize, right: usize) -> usize {
    left + right
}
```

```c++
#pragma once
#include <cstddef>

::std::size_t add(::std::size_t left, ::std::size_t right) noexcept;
```

### Footnotes

[0]: Or, is it an option? Well, we could absolutely reject workarounds during code review. However, this would be incapacitating. It means we cannot pass pointers/references to Rust without designing either a safety annotation system (so that we can pass pointers and dereference them in Rust), or else design safe aliasing reference wrappers (so that the function doesn't need to be unsafe at all). Both currently lack a solution and agreement, and will for some time still.

PiperOrigin-RevId: 592703897
Change-Id: I3b6ef341f92f995fd9c5aabd250b858b4b4f8ad0
diff --git a/cc_bindings_from_rs/test/functions/functions.rs b/cc_bindings_from_rs/test/functions/functions.rs
index 1cd10aa..0f44aa2 100644
--- a/cc_bindings_from_rs/test/functions/functions.rs
+++ b/cc_bindings_from_rs/test/functions/functions.rs
@@ -118,3 +118,13 @@
         x + y
     }
 }
+
+pub mod unsafe_fn_tests {
+    /// # Safety
+    ///
+    /// This function has no safety requirements - it is only marked as `unsafe`
+    /// to facilitate minimal testing of bindings generated for such functions.
+    pub unsafe fn unsafe_add(x: i32, y: i32) -> i32 {
+        x + y
+    }
+}