Repository: dtolnay/cxx (language: rust)

Issue:
Returning a `&*mut T` fails due to discards qualifiers and `&*const T` fails with duplicate const
<!--  ❤️  -->

Wanted to check if this is intentional or not, if you have a Rust method that returns a reference to a pointer such as `unsafe fn getter_ref<'a>(self: &'a RustObject) -> &'a *mut CppObject;` this fails to build with

```
In member function ‘CppObject* const& RustObject::getter_ref() const’:
error: binding reference of type ‘CppObject* const&’ to ‘const CppObject*’ discards qualifiers
```

Note it does work if it's not a reference and just a pointer, so can be worked around.

# Example code

This example code has a RustObject that contains a CppObject inside it. The idea here is the C++ has created the RustObject with the CppObject inside it, it then needs to access the inner CppObject and call something on it.

For non pointer types this is normally done via a getter which is a reference to the field, eg `&T` or `&mut T`, but when `&*mut T` is used this doesn't build with CXX.

src/main.rs
```rust
#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("cxxptrtest/include/object.h");
        type CppObject;

        fn cpp_run();
    }

    extern "Rust" {
        type RustObject;
        unsafe fn getter_ref<'a>(self: &'a RustObject) -> &'a *mut CppObject;
        unsafe fn getter_raw(self: &RustObject) -> *mut CppObject;

        unsafe fn new_rust_object(inner: *mut CppObject) -> Box<RustObject>;
    }
}

struct RustObject {
    inner: *mut ffi::CppObject,
}

impl RustObject {
    pub fn getter_ref(&self) -> &*mut ffi::CppObject {
        &self.inner
    }

    pub fn getter_raw(&self) -> *mut ffi::CppObject {
        self.inner
    }
}

fn new_rust_object(inner: *mut ffi::CppObject) -> Box<RustObject> {
    Box::new(RustObject { inner })
}

fn main() {
    ffi::cpp_run();
}
```

include/object.h
```cpp
#pragma once

#include <cstdint>

class CppObject
{
public:
  ::std::int32_t value() const;
};

void cpp_run();
```

src/object.cc
```cpp
#include "cxxptrtest/include/object.h"

#include "cxxptrtest/src/main.rs.h"

#include <iostream>

::std::int32_t
CppObject::value() const
{
  return 10;
}

void
cpp_run()
{
  // Works
  {
    auto rust = new_rust_object(new CppObject());
    std::cout << rust->getter_raw()->value() << std::endl;
  }

  // doesn't work
  {
    auto rust = new_rust_object(new CppObject());
    std::cout << rust->getter_ref()->value() << std::endl;
  }
}
```

# CXX Generated methods

main.rs.cc
```cpp
const ::CppObject * *cxxbridge1$RustObject$getter_ref(const ::RustObject &self) noexcept;

::CppObject *const &RustObject::getter_ref() const noexcept {
  return *cxxbridge1$RustObject$getter_ref(*this);
}
```

The compiler fails inside the getter_ref with
```
warning: cxxptrtest/target/debug/build/cxxptrtest-8757e40d155cc53a/out/cxxbridge/sources/cxxptrtest/src/main.rs.cc: In member function ‘CppObject* const& RustObject::getter_ref() const’:
warning:cxxptrtest/target/debug/build/cxxptrtest-8757e40d155cc53a/out/cxxbridge/sources/cxxptrtest/src/main.rs.cc:290:10: error: binding reference of type ‘CppObject* const&’ to ‘const CppObject*’ discards qualifiers
warning:   290 |   return *cxxbridge1$RustObject$getter_ref(*this);
warning:       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

# `&*const T`

Furthermore if you change the `getter_ref` to return `&*const T` you get

```
error: duplicate ‘const’
const const ::CppObject * *cxxbridge1$RustObject$getter_ref(const ::RustObject &self) noexcept;
```

CXX seems to have generated

```cpp
const const ::CppObject * *cxxbridge1$RustObject$getter_ref(const ::RustObject &self) noexcept;

const ::CppObject *const &RustObject::getter_ref() const noexcept {
  return *cxxbridge1$RustObject$getter_ref(*this);
}
```

# Mutable references

Note that returning a `&mut *mut T` or `&mut *const T` does work.

So I guess my question is, should `&*mut T` and `&*const T` work with CXX or are these expected not to work ?


Discussion / hints:

Fix patch:
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index eb892d37d..277c64f8d 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -264,8 +264,8 @@ pub(super) fn write(out: &mut OutFile) {
             out,
             "  Fail(::rust::repr::PtrLen &throw$) noexcept : throw$(throw$) {{}}",
         );
-        writeln!(out, "  void operator()(const char *) noexcept;");
-        writeln!(out, "  void operator()(const std::string &) noexcept;");
+        writeln!(out, "  void operator()(char const *) noexcept;");
+        writeln!(out, "  void operator()(std::string const &) noexcept;");
         writeln!(out, "}};");
     }
 
@@ -352,7 +352,7 @@ pub(super) fn write(out: &mut OutFile) {
         writeln!(out, "public:");
         writeln!(out, "  static Error error(repr::PtrLen repr) noexcept {{");
         writeln!(out, "    Error error;");
-        writeln!(out, "    error.msg = static_cast<const char *>(repr.ptr);");
+        writeln!(out, "    error.msg = static_cast<char const *>(repr.ptr);");
         writeln!(out, "    error.len = repr.len;");
         writeln!(out, "    return error;");
         writeln!(out, "  }}");
@@ -412,7 +412,7 @@ pub(super) fn write(out: &mut OutFile) {
         writeln!(out, "                 missing>::value>::type");
         writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
         writeln!(out, "  func();");
-        writeln!(out, "}} catch (const ::std::exception &e) {{");
+        writeln!(out, "}} catch (::std::exception const &e) {{");
         writeln!(out, "  fail(e.what());");
         writeln!(out, "}}");
         out.end_block(Block::Namespace("behavior"));
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 22a0d4b9b..6f535ccb9 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -177,7 +177,7 @@ fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
                 writeln!(out, "template <> struct hash<{}> {{", qualified);
                 writeln!(
                     out,
-                    "  ::std::size_t operator()(const {} &self) const noexcept {{",
+                    "  ::std::size_t operator()({} const &self) const noexcept {{",
                     qualified,
                 );
                 let link_name = mangle::operator(&strct.name, "hash");
@@ -285,12 +285,12 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern
     if operator_eq {
         writeln!(
             out,
-            "  bool operator==(const {} &) const noexcept;",
+            "  bool operator==({} const &) const noexcept;",
             strct.name.cxx,
         );
         writeln!(
             out,
-            "  bool operator!=(const {} &) const noexcept;",
+            "  bool operator!=({} const &) const noexcept;",
             strct.name.cxx,
         );
     }
@@ -298,22 +298,22 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern
     if operator_ord {
         writeln!(
             out,
-            "  bool operator<(const {} &) const noexcept;",
+            "  bool operator<({} const &) const noexcept;",
             strct.name.cxx,
         );
         writeln!(
             out,
-            "  bool operator<=(const {} &) const noexcept;",
+            "  bool operator<=({} const &) const noexcept;",
             strct.name.cxx,
         );
         writeln!(
             out,
-            "  bool operator>(const {} &) const noexcept;",
+            "  bool operator>({} const &) const noexcept;",
             strct.name.cxx,
         );
         writeln!(
             out,
-            "  bool operator>=(const {} &) const noexcept;",
+            "  bool operator>=({} const &) const noexcept;",
             strct.name.cxx,
         );
     }
@@ -510,7 +510,7 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         let link_name = mangle::operator(&strct.name, "eq");
         writeln!(
             out,
-            "bool {}(const {1} &, const {1} &) noexcept;",
+            "bool {}({1} const &, {1} const &) noexcept;",
             link_name, strct.name.cxx,
         );
 
@@ -518,7 +518,7 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
             let link_name = mangle::operator(&strct.name, "ne");
             writeln!(
                 out,
-                "bool {}(const {1} &, const {1} &) noexcept;",
+                "bool {}({1} const &, {1} const &) noexcept;",
                 link_name, strct.name.cxx,
             );
         }
@@ -528,14 +528,14 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         let link_name = mangle::operator(&strct.name, "lt");
         writeln!(
             out,
-            "bool {}(const {1} &, const {1} &) noexcept;",
+            "bool {}({1} const &, {1} const &) noexcept;",
             link_name, strct.name.cxx,
         );
 
         let link_name = mangle::operator(&strct.name, "le");
         writeln!(
             out,
-            "bool {}(const {1} &, const {1} &) noexcept;",
+            "bool {}({1} const &, {1} const &) noexcept;",
             link_name, strct.name.cxx,
         );
 
@@ -543,14 +543,14 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
             let link_name = mangle::operator(&strct.name, "gt");
             writeln!(
                 out,
-                "bool {}(const {1} &, const {1} &) noexcept;",
+                "bool {}({1} const &, {1} const &) noexcept;",
                 link_name, strct.name.cxx,
             );
 
             let link_name = mangle::operator(&strct.name, "ge");
             writeln!(
                 out,
-                "bool {}(const {1} &, const {1} &) noexcept;",
+                "bool {}({1} const &, {1} const &) noexcept;",
                 link_name, strct.name.cxx,
             );
         }
@@ -561,7 +561,7 @@ fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         let link_name = mangle::operator(&strct.name, "hash");
         writeln!(
             out,
-            "::std::size_t {}(const {} &) noexcept;",
+            "::std::size_t {}({} const &) noexcept;",
             link_name, strct.name.cxx,
         );
     }
@@ -580,7 +580,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator==(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator==({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         let link_name = mangle::operator(&strct.name, "eq");
@@ -590,7 +590,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator!=(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator!=({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         if derive::contains(&strct.derives, Trait::Eq) {
@@ -606,7 +606,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator<(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator<({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         let link_name = mangle::operator(&strct.name, "lt");
@@ -616,7 +616,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator<=(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator<=({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         let link_name = mangle::operator(&strct.name, "le");
@@ -626,7 +626,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator>(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator>({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         if derive::contains(&strct.derives, Trait::Ord) {
@@ -640,7 +640,7 @@ fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
         out.next_section();
         writeln!(
             out,
-            "bool {0}::operator>=(const {0} &rhs) const noexcept {{",
+            "bool {0}::operator>=({0} const &rhs) const noexcept {{",
             strct.name.cxx,
         );
         if derive::contains(&strct.derives, Trait::Ord) {
@@ -714,25 +714,29 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
     let mangled = mangle::extern_fn(efn, out.types);
     write!(out, "{}(", mangled);
     if let Some(receiver) = &efn.receiver {
-        if !receiver.mutable {
-            write!(out, "const ");
-        }
         write!(
             out,
-            "{} &self",
+            "{}",
             out.types.resolve(&receiver.ty).name.to_fully_qualified(),
         );
+        if !receiver.mutable {
+            write!(out, " const");
+        }
+        write!(out, " &self");
     }
     for (i, arg) in efn.args.iter().enumerate() {
         if i > 0 || efn.receiver.is_some() {
             write!(out, ", ");
         }
         if arg.ty == RustString {
-            write!(out, "const ");
+            write_type_space(out, &arg.ty);
+            write!(out, "const *{}", arg.name.cxx);
         } else if let Type::RustVec(_) = arg.ty {
-            write!(out, "const ");
+            write_type_space(out, &arg.ty);
+            write!(out, "const *{}", arg.name.cxx);
+        } else {
+            write_extern_arg(out, arg);
         }
-        write_extern_arg(out, arg);
     }
     let indirect_return = indirect_return(efn, out.types);
     if indirect_return {
@@ -903,14 +907,15 @@ fn write_rust_function_decl_impl(
     write!(out, "{}(", link_name);
     let mut needs_comma = false;
     if let Some(receiver) = &sig.receiver {
-        if !receiver.mutable {
-            write!(out, "const ");
-        }
         write!(
             out,
-            "{} &self",
+            "{}",
             out.types.resolve(&receiver.ty).name.to_fully_qualified(),
         );
+        if !receiver.mutable {
+            write!(out, " const");
+        }
+        write!(out, " &self");
         needs_comma = true;
     }
     for arg in &sig.args {
@@ -926,8 +931,11 @@ fn write_rust_function_decl_impl(
         }
         match sig.ret.as_ref().unwrap() {
             Type::Ref(ret) => {
-                write_pointee_type(out, &ret.inner, ret.mutable);
-                write!(out, " *");
+                write_type_space(out, &ret.inner);
+                if !ret.mutable {
+                    write!(out, "const ");
+                }
+                write!(out, "*");
             }
             ret => write_type_space(out, ret),
         }
@@ -1030,8 +1038,11 @@ fn write_rust_function_shim_impl(
         write!(out, "::rust::MaybeUninit<");
         match sig.ret.as_ref().unwrap() {
             Type::Ref(ret) => {
-                write_pointee_type(out, &ret.inner, ret.mutable);
-                write!(out, " *");
+                write_type_space(out, &ret.inner);
+                if !ret.mutable {
+                    write!(out, "const ");
+                }
+                write!(out, "*");
             }
             ret => write_type(out, ret),
         }
@@ -1150,11 +1161,11 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
             write!(out, "*");
         }
         Type::Ref(ty) => {
+            write_type_space(out, &ty.inner);
             if !ty.mutable {
                 write!(out, "const ");
             }
-            write_type(out, &ty.inner);
-            write!(out, " *");
+            write!(out, "*");
         }
         _ => write_type(out, ty),
     }
@@ -1176,11 +1187,11 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
             write!(out, "*");
         }
         Some(Type::Ref(ty)) => {
+            write_type_space(out, &ty.inner);
             if !ty.mutable {
                 write!(out, "const ");
             }
-            write_type(out, &ty.inner);
-            write!(out, " *");
+            write!(out, "*");
         }
         Some(Type::Str(_)) | Some(Type::SliceRef(_)) => {
             out.builtin.repr_fat = true;
@@ -1246,22 +1257,28 @@ fn write_type(out: &mut OutFile, ty: &Type) {
             write!(out, ">");
         }
         Type::Ref(r) => {
-            write_pointee_type(out, &r.inner, r.mutable);
-            write!(out, " &");
+            write_type_space(out, &r.inner);
+            if !r.mutable {
+                write!(out, "const ");
+            }
+            write!(out, "&");
         }
         Type::Ptr(p) => {
-            write_pointee_type(out, &p.inner, p.mutable);
-            write!(out, " *");
+            write_type_space(out, &p.inner);
+            if !p.mutable {
+                write!(out, "const ");
+            }
+            write!(out, "*");
         }
         Type::Str(_) => {
             write!(out, "::rust::Str");
         }
         Type::SliceRef(slice) => {
             write!(out, "::rust::Slice<");
+            write_type_space(out, &slice.inner);
             if slice.mutability.is_none() {
-                write!(out, "const ");
+                write!(out, "const");
             }
-            write_type(out, &slice.inner);
             write!(out, ">");
         }
         Type::Fn(f) => {
@@ -1288,21 +1305,6 @@ 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"),
@@ -1460,7 +1462,7 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
 
     writeln!(
         out,
-        "void cxxbridge1$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
+        "void cxxbridge1$rust_vec${}$new(::rust::Vec<{}> const *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
@@ -1470,17 +1472,17 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
     );
     writeln!(
         out,
-        "::std::size_t cxxbridge1$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+        "::std::size_t cxxbridge1$rust_vec${}$len(::rust::Vec<{}> const *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "::std::size_t cxxbridge1$rust_vec${}$capacity(const ::rust::Vec<{}> *ptr) noexcept;",
+        "::std::size_t cxxbridge1$rust_vec${}$capacity(::rust::Vec<{}> const *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "const {} *cxxbridge1$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+        "{} const *cxxbridge1$rust_vec${}$data(::rust::Vec<{0}> const *ptr) noexcept;",
         inner, instance,
     );
     writeln!(
@@ -1577,7 +1579,7 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
 
     writeln!(out, "template <>");
     begin_function_definition(out);
-    writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+    writeln!(out, "{} const *Vec<{0}>::data() const noexcept {{", inner);
     writeln!(out, "  return cxxbridge1$rust_vec${}$data(this);", instance);
     writeln!(out, "}}");
 
@@ -1706,7 +1708,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
     begin_function_definition(out);
     writeln!(
         out,
-        "const {} *cxxbridge1$unique_ptr${}$get(const ::std::unique_ptr<{}> &ptr) noexcept {{",
+        "{} const *cxxbridge1$unique_ptr${}$get(::std::unique_ptr<{}> const &ptr) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return ptr.get();");
@@ -1791,7 +1793,7 @@ fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
     begin_function_definition(out);
     writeln!(
         out,
-        "void cxxbridge1$shared_ptr${}$clone(const ::std::shared_ptr<{}> &self, ::std::shared_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge1$shared_ptr${}$clone(::std::shared_ptr<{}> const &self, ::std::shared_ptr<{}> *ptr) noexcept {{",
         instance, inner, inner,
     );
     writeln!(out, "  ::new (ptr) ::std::shared_ptr<{}>(self);", inner);
@@ -1799,7 +1801,7 @@ fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
     begin_function_definition(out);
     writeln!(
         out,
-        "const {} *cxxbridge1$shared_ptr${}$get(const ::std::shared_ptr<{}> &self) noexcept {{",
+        "{} const *cxxbridge1$shared_ptr${}$get(::std::shared_ptr<{}> const &self) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return self.get();");
@@ -1842,7 +1844,7 @@ fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
     begin_function_definition(out);
     writeln!(
         out,
-        "void cxxbridge1$weak_ptr${}$clone(const ::std::weak_ptr<{}> &self, ::std::weak_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge1$weak_ptr${}$clone(::std::weak_ptr<{}> const &self, ::std::weak_ptr<{}> *ptr) noexcept {{",
         instance, inner, inner,
     );
     writeln!(out, "  ::new (ptr) ::std::weak_ptr<{}>(self);", inner);
@@ -1850,7 +1852,7 @@ fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
     begin_function_definition(out);
     writeln!(
         out,
-        "void cxxbridge1$weak_ptr${}$downgrade(const ::std::shared_ptr<{}> &shared, ::std::weak_ptr<{}> *weak) noexcept {{",
+        "void cxxbridge1$weak_ptr${}$downgrade(::std::shared_ptr<{}> const &shared, ::std::weak_ptr<{}> *weak) noexcept {{",
         instance, inner, inner,
     );
     writeln!(out, "  ::new (weak) ::std::weak_ptr<{}>(shared);", inner);
@@ -1858,7 +1860,7 @@ fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
     begin_function_definition(out);
     writeln!(
         out,
-        "void cxxbridge1$weak_ptr${}$upgrade(const ::std::weak_ptr<{}> &weak, ::std::shared_ptr<{}> *shared) noexcept {{",
+        "void cxxbridge1$weak_ptr${}$upgrade(::std::weak_ptr<{}> const &weak, ::std::shared_ptr<{}> *shared) noexcept {{",
         instance, inner, inner,
     );
     writeln!(
@@ -1888,7 +1890,7 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
 
     writeln!(
         out,
-        "::std::size_t cxxbridge1$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+        "::std::size_t cxxbridge1$std$vector${}$size(::std::vector<{}> const &s) noexcept {{",
         instance, inner,
     );
     writeln!(out, "  return s.size();");


Test patch: