Repository: dtolnay/cxx (language: rust)

Issue:
Reference return type from fallible Rust function does not compile
```rust
use anyhow::Result;

#[cxx::bridge]
mod ffi {
    extern "Rust" {
        fn test() -> Result<&'static i32>;
    }
}

fn test() -> Result<&'static i32> {
    Ok(&0)
}
```

The return type is being implemented by a pointer out-parameter, and it's not possible for that pointer to point to a reference in C++.

```console
$ cargo check
target/debug/build/example-283878198746b170/out/cxxbridge/sources/example/src/main.rs.cc:80:60: error: 'return$' declared as a pointer to a reference of type 'const ::std::int32_t &' (aka 'const int &')
::rust::repr::PtrLen cxxbridge1$test(const ::std::int32_t &*return$) noexcept;
                                                           ^
target/debug/build/example-283878198746b170/out/cxxbridge/sources/example/src/main.rs.cc:51:5: error: union member 'value' has reference type 'const int &'
  T value;
    ^
target/debug/build/example-283878198746b170/out/cxxbridge/sources/example/src/main.rs.cc:84:47: note: in instantiation of template class 'rust::cxxbridge1::MaybeUninit<const int &>' requested here
  ::rust::MaybeUninit<const ::std::int32_t &> return$;
                                              ^
```


Discussion / hints:

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 930ce0b79..7b0b49b39 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -873,7 +873,13 @@ fn write_rust_function_decl_impl(
         if needs_comma {
             write!(out, ", ");
         }
-        write_return_type(out, &sig.ret);
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(ret) => {
+                write_pointee_type(out, &ret.inner, ret.mutable);
+                write!(out, " *");
+            }
+            ret => write_type_space(out, ret),
+        }
         write!(out, "*return$");
         needs_comma = true;
     }
@@ -968,7 +974,13 @@ fn write_rust_function_shim_impl(
     if indirect_return {
         out.builtin.maybe_uninit = true;
         write!(out, "::rust::MaybeUninit<");
-        write_type(out, sig.ret.as_ref().unwrap());
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(ret) => {
+                write_pointee_type(out, &ret.inner, ret.mutable);
+                write!(out, " *");
+            }
+            ret => write_type(out, ret),
+        }
         writeln!(out, "> return$;");
         write!(out, "  ");
     } else if let Some(ret) = &sig.ret {
@@ -1051,8 +1063,15 @@ fn write_rust_function_shim_impl(
         writeln!(out, "  }}");
     }
     if indirect_return {
-        out.include.utility = true;
-        writeln!(out, "  return ::std::move(return$.value);");
+        write!(out, "  return ");
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(_) => write!(out, "*return$.value"),
+            _ => {
+                out.include.utility = true;
+                write!(out, "::std::move(return$.value)");
+            }
+        }
+        writeln!(out, ";");
     }
     writeln!(out, "}}");
 }
@@ -1173,31 +1192,11 @@ fn write_type(out: &mut OutFile, ty: &Type) {
             write!(out, ">");
         }
         Type::Ref(r) => {
-            if let Type::Ptr(_) = r.inner {
-                write_type_space(out, &r.inner);
-                if !r.mutable {
-                    write!(out, "const");
-                }
-            } else {
-                if !r.mutable {
-                    write!(out, "const ");
-                }
-                write_type(out, &r.inner);
-            }
+            write_pointee_type(out, &r.inner, r.mutable);
             write!(out, " &");
         }
         Type::Ptr(p) => {
-            if let Type::Ptr(_) = p.inner {
-                write_type_space(out, &p.inner);
-                if !p.mutable {
-                    write!(out, "const");
-                }
-            } else {
-                if !p.mutable {
-                    write!(out, "const ");
-                }
-                write_type(out, &p.inner);
-            }
+            write_pointee_type(out, &p.inner, p.mutable);
             write!(out, " *");
         }
         Type::Str(_) => {
@@ -1235,6 +1234,21 @@ fn write_type(out: &mut OutFile, ty: &Type) {
     }
 }
 
+// Write just the T type behind a &T or &mut T or *const T or *mut T.
+fn write_pointee_type(out: &mut OutFile, inner: &Type, mutable: bool) {
+    if let Type::Ptr(_) = inner {
+        write_type_space(out, inner);
+        if !mutable {
+            write!(out, "const");
+        }
+    } else {
+        if !mutable {
+            write!(out, "const ");
+        }
+        write_type(out, inner);
+    }
+}
+
 fn write_atom(out: &mut OutFile, atom: Atom) {
     match atom {
         Bool => write!(out, "bool"),


Test patch: