Allow function pointers with references inside the function signature.

Without this change, because we change the reference to a pointer, the type doesn't line up:

```
error: cannot initialize return object of type 'crubit::t
ype_identity_t<int *(const int *, int *)> *' (aka 'int *(*)(const int *, int *)') with an rvalue of type 'int &(*)(const int &, int *)': type mismatch at 1st parameter
('const int *' vs 'const int &')
```

I was worried this would mean we'd have trouble with `-Wreturn-type-c-linkage`, but surprisingly, it works fine:

https://godbolt.org/z/njabjEfTK

(As you can see, and as the name implies, it also works fine with reference parameters, but presumably there's another warning that fails there.)

I didn't add any other tests because really "does it compile?" is the main thing. This failed before, succeeds now.

PiperOrigin-RevId: 478958712
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 91f25f6..1a9c82e 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -3103,6 +3103,13 @@
 }
 
 fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
+    // Formatting *both* pointers *and* references as pointers, because:
+    // - Pointers and references have the same representation in the ABI.
+    // - Clang's `-Wreturn-type-c-linkage` warns when using references in C++
+    //   function thunks declared as `extern "C"` (see b/238681766).
+    format_cc_type_inner(ty, ir, /* references_ok= */ false)
+}
+fn format_cc_type_inner(ty: &ir::CcType, ir: &IR, references_ok: bool) -> Result<TokenStream> {
     let const_fragment = if ty.is_const {
         quote! {const}
     } else {
@@ -3110,16 +3117,21 @@
     };
     if let Some(ref name) = ty.name {
         match name.as_str() {
-            // Formatting *both* pointers *and* references as pointers, because:
-            // - Pointers and references have the same representation in the ABI.
-            // - Clang's `-Wreturn-type-c-linkage` warns when using references in C++ function
-            //   thunks declared as `extern "C"` (see b/238681766).
-            "*" | "&" | "&&" => {
+            mut name @ ("*" | "&" | "&&") => {
                 if ty.type_args.len() != 1 {
                     bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
                 }
-                let nested_type = format_cc_type(&ty.type_args[0], ir)?;
-                Ok(quote! {#nested_type * #const_fragment})
+                let nested_type = format_cc_type_inner(&ty.type_args[0], ir, references_ok)?;
+                if !references_ok {
+                    name = "*";
+                }
+                let ptr = match name {
+                    "*" => quote! {*},
+                    "&" => quote! {&},
+                    "&&" => quote! {&&},
+                    _ => unreachable!(),
+                };
+                Ok(quote! {#nested_type #ptr #const_fragment})
             }
             cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
                 None => {
@@ -3132,10 +3144,14 @@
                 Some(abi) => match ty.type_args.split_last() {
                     None => bail!("funcValue type without a return type: {:?}", ty),
                     Some((ret_type, param_types)) => {
-                        let ret_type = format_cc_type(ret_type, ir)?;
+                        // Function pointer types don't ignore references, but luckily,
+                        // `-Wreturn-type-c-linkage` does. So we can just re-enable references now
+                        // so that the function type is exactly correct.
+                        let ret_type =
+                            format_cc_type_inner(ret_type, ir, /* references_ok= */ true)?;
                         let param_types = param_types
                             .iter()
-                            .map(|t| format_cc_type(t, ir))
+                            .map(|t| format_cc_type_inner(t, ir, /* references_ok= */ true))
                             .collect::<Result<Vec<_>>>()?;
                         let attr = format_cc_call_conv_as_clang_attribute(abi)?;
                         // `type_identity_t` is used below to avoid having to
diff --git a/rs_bindings_from_cc/test/golden/types.h b/rs_bindings_from_cc/test/golden/types.h
index 881efd9..fbe42cc 100644
--- a/rs_bindings_from_cc/test/golden/types.h
+++ b/rs_bindings_from_cc/test/golden/types.h
@@ -87,4 +87,12 @@
 
 enum Color : unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };
 
+// Note especially the use of references. If we convert those to pointers,
+// this becomes un-compilable. The syntax here is awful, but this is a function
+// returning a function. In ML-like syntax:
+// FunctionPointerReturningFunction : () -> (const int&, int*) -> int&
+inline int& (*FunctionPointerReturningFunction())(const int&, int*) {
+  return nullptr;
+}
+
 #endif  // CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TYPES_H_
diff --git a/rs_bindings_from_cc/test/golden/types_rs_api.rs b/rs_bindings_from_cc/test/golden/types_rs_api.rs
index 90f2102..c823bcd 100644
--- a/rs_bindings_from_cc/test/golden/types_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/types_rs_api.rs
@@ -153,6 +153,16 @@
     }
 }
 
+/// Note especially the use of references. If we convert those to pointers,
+/// this becomes un-compilable. The syntax here is awful, but this is a function
+/// returning a function. In ML-like syntax:
+/// FunctionPointerReturningFunction : () -> (const int&, int*) -> int&
+#[inline(always)]
+pub fn FunctionPointerReturningFunction() -> Option<extern "C" fn(*const i32, *mut i32) -> *mut i32>
+{
+    unsafe { crate::detail::__rust_thunk___Z32FunctionPointerReturningFunctionv() }
+}
+
 // CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TYPES_H_
 
 #[::ctor::recursively_pinned]
@@ -263,6 +273,8 @@
             __param_0: ::ctor::RvalueReference<'b, crate::FieldTypeTestStruct>,
         );
         pub(crate) fn __rust_thunk___Z21VoidReturningFunctionv();
+        pub(crate) fn __rust_thunk___Z32FunctionPointerReturningFunctionv()
+        -> Option<extern "C" fn(*const i32, *mut i32) -> *mut i32>;
     }
 }
 
diff --git a/rs_bindings_from_cc/test/golden/types_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/types_rs_api_impl.cc
index 15f2efb..9003fca 100644
--- a/rs_bindings_from_cc/test/golden/types_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/golden/types_rs_api_impl.cc
@@ -25,6 +25,10 @@
 extern "C" void __rust_thunk___Z21VoidReturningFunctionv() {
   VoidReturningFunction();
 }
+extern "C" crubit::type_identity_t<int&(int const&, int*)>*
+__rust_thunk___Z32FunctionPointerReturningFunctionv() {
+  return FunctionPointerReturningFunction();
+}
 
 static_assert(sizeof(struct std::integral_constant<bool, false>) == 1);
 static_assert(alignof(struct std::integral_constant<bool, false>) == 1);