Repository: dtolnay/cxx (language: rust)

Issue:
Allow mut from ref in unsafe-to-call signatures
Currently signatures that return a mutable reference but do not take any mutable reference arguments are disallowed. It should be fine to allow such signatures as long as they are made unsafe to call.

Current behavior:

```rust
#[cxx::bridge]
mod ffi {
    extern "C++" {
        type Thing;

        unsafe fn f(t: &Thing) -> Pin<&mut CxxString>;
    }
}
```

```console
error[cxxbridge]: &mut return type is not allowed unless there is a &mut argument
  ┌─ src/main.rs:6:16
  │
6 │         unsafe fn f(t: &Thing) -> Pin<&mut CxxString>;
  │                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ &mut return type is not allowed unless there is a &mut argument
```


Discussion / hints:

Fix patch:
diff --git a/syntax/check.rs b/syntax/check.rs
index 6ba65650c..1e52c2e77 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -508,6 +508,11 @@ fn check_api_impl(cx: &mut Check, imp: &Impl) {
 }
 
 fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
+    if efn.sig.unsafety.is_some() {
+        // Unrestricted as long as the function is made unsafe-to-call.
+        return;
+    }
+
     match &efn.ret {
         Some(Type::Ref(ty)) if ty.mutable => {}
         _ => return,
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 13b5cd3c7..c1a06a213 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -204,6 +204,7 @@ impl ToTokens for Enum {
 impl ToTokens for ExternFn {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
+        self.unsafety.to_tokens(tokens);
         self.sig.fn_token.to_tokens(tokens);
         self.semi_token.to_tokens(tokens);
     }


Test patch:
diff --git a/tests/ui/mut_return.rs b/tests/ui/mut_return.rs
new file mode 100644
index 000000000..74a5c6960
--- /dev/null
+++ b/tests/ui/mut_return.rs
@@ -0,0 +1,11 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type Thing;
+
+        fn f(t: &Thing) -> Pin<&mut CxxString>;
+        unsafe fn g(t: &Thing) -> Pin<&mut CxxString>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/mut_return.stderr b/tests/ui/mut_return.stderr
new file mode 100644
index 000000000..d6ddb88b8
--- /dev/null
+++ b/tests/ui/mut_return.stderr
@@ -0,0 +1,5 @@
+error: &mut return type is not allowed unless there is a &mut argument
+ --> $DIR/mut_return.rs:6:9
+  |
+6 |         fn f(t: &Thing) -> Pin<&mut CxxString>;
+  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^