Repository: dtolnay/cxx (rust)

Issue:
Order sensitivity across namespaces
The namespace sorting introduced in #370 does not handle alphabetically lesser namespaces referencing items from alphabetically greater namespaces.

```rust
#[cxx::bridge]
mod ffi {
    #[namespace = "first"]
    struct First {
        second: Box<Second>,
    }

    #[namespace = "second"]
    struct Second {
        i: i32,
    }
}
```

```console
target/debug/build/demo-d74fe843f72e57b6/out/cxxbridge/sources/demo/src/main.rs.cc:154:17: error: ‘::second’ has not been declared
  154 |   ::rust::Box<::second::Second> second;
      |                 ^~~~~~
target/debug/build/demo-d74fe843f72e57b6/out/cxxbridge/sources/demo/src/main.rs.cc:154:31: error: template argument 1 is invalid
  154 |   ::rust::Box<::second::Second> second;
      |                               ^
```

Pull request description:
Namespace forward declarations
Fixes #386.

Patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index e612beed..dfdade22 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -32,6 +32,7 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -
 
     let apis_by_namespace = NamespaceEntries::new(apis);
 
+    gen_namespace_forward_declarations(&apis_by_namespace, out);
     gen_namespace_contents(&apis_by_namespace, types, opt, header, out);
 
     if !header {
@@ -44,17 +45,11 @@ pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -
     out_file
 }
 
-fn gen_namespace_contents(
-    ns_entries: &NamespaceEntries,
-    types: &Types,
-    opt: &Opt,
-    header: bool,
-    out: &mut OutFile,
-) {
+fn gen_namespace_forward_declarations(ns_entries: &NamespaceEntries, out: &mut OutFile) {
     let apis = ns_entries.entries();
 
     out.next_section();
-    for api in apis {
+    for api in apis.iter() {
         match api {
             Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident),
             Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx),
@@ -63,8 +58,26 @@ fn gen_namespace_contents(
         }
     }
 
+    out.next_section();
+
+    for (child_ns, child_ns_entries) in ns_entries.children() {
+        writeln!(out, "namespace {} {{", child_ns);
+        gen_namespace_forward_declarations(&child_ns_entries, out);
+        writeln!(out, "}} // namespace {}", child_ns);
+    }
+}
+
+fn gen_namespace_contents(
+    ns_entries: &NamespaceEntries,
+    types: &Types,
+    opt: &Opt,
+    header: bool,
+    out: &mut OutFile,
+) {
+    let apis = ns_entries.entries();
+
     let mut methods_for_type = HashMap::new();
-    for api in apis {
+    for api in apis.iter() {
         if let Api::RustFunction(efn) = api {
             if let Some(receiver) = &efn.sig.receiver {
                 methods_for_type
@@ -214,21 +227,20 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) {
     let mut needs_manually_drop = false;
     let mut needs_maybe_uninit = false;
     let mut needs_trycatch = false;
-    let mut needs_rust_str_new_unchecked = false;
-    let mut needs_rust_str_repr = false;
     for api in apis {
         match api {
             Api::CxxFunction(efn) if !out.header => {
                 if efn.throws {
                     needs_trycatch = true;
-                } else if let Some(Type::Str(_)) = efn.ret {
-                    needs_rust_str_repr = true;
                 }
                 for arg in &efn.args {
-                    match arg.ty {
-                        Type::Str(_) => needs_rust_str_new_unchecked = true,
-                        Type::RustVec(_) => needs_unsafe_bitcopy = true,
-                        _ => needs_unsafe_bitcopy |= arg.ty == RustString,
+                    let bitcopy = match arg.ty {
+                        Type::RustVec(_) => true,
+                        _ => arg.ty == RustString,
+                    };
+                    if bitcopy {
+                        needs_unsafe_bitcopy = true;
+                        break;
                     }
                 }
             }
@@ -243,16 +255,12 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) {
                 for arg in &efn.args {
                     if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
                         needs_manually_drop = true;
-                    }
-                    if let Type::Str(_) = arg.ty {
-                        needs_rust_str_repr = true;
+                        break;
                     }
                 }
                 if let Some(ret) = &efn.ret {
                     if out.types.needs_indirect_abi(ret) {
                         needs_maybe_uninit = true;
-                    } else if let Type::Str(_) = ret {
-                        needs_rust_str_new_unchecked = true;
                     }
                 }
             }
@@ -286,13 +294,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) {
         writeln!(out, "struct unsafe_bitcopy_t;");
     }
 
-    if needs_rust_error {
-        out.begin_block("namespace");
-        writeln!(out, "template <typename T>");
-        writeln!(out, "class impl;");
-        out.end_block("namespace");
-    }
-
     include::write(out, needs_rust_string, "CXXBRIDGE05_RUST_STRING");
     include::write(out, needs_rust_str, "CXXBRIDGE05_RUST_STR");
     include::write(out, needs_rust_slice, "CXXBRIDGE05_RUST_SLICE");
@@ -327,56 +328,6 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api]) {
         writeln!(out, "}};");
     }
 
-    out.begin_block("namespace");
-
-    if needs_trycatch || needs_rust_error || needs_rust_str_new_unchecked || needs_rust_str_repr {
-        out.begin_block("namespace repr");
-        writeln!(out, "struct PtrLen final {{");
-        writeln!(out, "  const void *ptr;");
-        writeln!(out, "  size_t len;");
-        writeln!(out, "}};");
-        out.end_block("namespace repr");
-    }
-
-    if needs_rust_str_new_unchecked || needs_rust_str_repr {
-        out.next_section();
-        writeln!(out, "template <>");
-        writeln!(out, "class impl<Str> final {{");
-        writeln!(out, "public:");
-        if needs_rust_str_new_unchecked {
-            writeln!(
-                out,
-                "  static Str new_unchecked(repr::PtrLen repr) noexcept {{",
-            );
-            writeln!(out, "    Str str;");
-            writeln!(out, "    str.ptr = static_cast<const char *>(repr.ptr);");
-            writeln!(out, "    str.len = repr.len;");
-            writeln!(out, "    return str;");
-            writeln!(out, "  }}");
-        }
-        if needs_rust_str_repr {
-            writeln!(out, "  static repr::PtrLen repr(Str str) noexcept {{");
-            writeln!(out, "    return repr::PtrLen{{str.ptr, str.len}};");
-            writeln!(out, "  }}");
-        }
-        writeln!(out, "}};");
-    }
-
-    if needs_rust_error {
-        out.next_section();
-        writeln!(out, "template <>");
-        writeln!(out, "class impl<Error> final {{");
-        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.len = repr.len;");
-        writeln!(out, "    return error;");
-        writeln!(out, "  }}");
-        writeln!(out, "}};");
-    }
-
-    out.end_block("namespace");
     out.end_block("namespace cxxbridge05");
 
     if needs_trycatch {
@@ -558,11 +509,13 @@ fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) {
 }
 
 fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option<String>) {
-    if let Some(annotation) = impl_annotations {
-        write!(out, "{} ", annotation);
+    if !out.header {
+        if let Some(annotation) = impl_annotations {
+            write!(out, "{} ", annotation);
+        }
     }
     if efn.throws {
-        write!(out, "::rust::repr::PtrLen ");
+        write!(out, "::rust::Str::Repr ");
     } else {
         write_extern_return_type_space(out, &efn.ret);
     }
@@ -634,7 +587,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations:
     writeln!(out, ";");
     write!(out, "  ");
     if efn.throws {
-        writeln!(out, "::rust::repr::PtrLen throw$;");
+        writeln!(out, "::rust::Str::Repr throw$;");
         writeln!(out, "  ::rust::behavior::trycatch(");
         writeln!(out, "      [&] {{");
         write!(out, "        ");
@@ -649,7 +602,7 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations:
     }
     match &efn.ret {
         Some(Type::Ref(_)) => write!(out, "&"),
-        Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::impl<::rust::Str>::repr("),
+        Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("),
         Some(Type::SliceRefU8(_)) if !indirect_return => {
             write!(out, "::rust::Slice<uint8_t>::Repr(")
         }
@@ -669,12 +622,6 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations:
         } else if let Type::UniquePtr(_) = &arg.ty {
             write_type(out, &arg.ty);
             write!(out, "({})", arg.ident);
-        } else if let Type::Str(_) = arg.ty {
-            write!(
-                out,
-                "::rust::impl<::rust::Str>::new_unchecked({})",
-                arg.ident,
-            );
         } else if arg.ty == RustString {
             write!(
                 out,
@@ -753,7 +700,7 @@ fn write_rust_function_decl_impl(
     indirect_call: bool,
 ) {
     if sig.throws {
-        write!(out, "::rust::repr::PtrLen ");
+        write!(out, "::rust::Str::Repr ");
     } else {
         write_extern_return_type_space(out, &sig.ret);
     }
@@ -887,12 +834,11 @@ fn write_rust_function_shim_impl(
                 write!(out, "(");
             }
             Type::Ref(_) => write!(out, "*"),
-            Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::new_unchecked("),
             _ => {}
         }
     }
     if sig.throws {
-        write!(out, "::rust::repr::PtrLen error$ = ");
+        write!(out, "::rust::Str::Repr error$ = ");
     }
     write!(out, "{}(", invoke);
     if sig.receiver.is_some() {
@@ -903,7 +849,7 @@ fn write_rust_function_shim_impl(
             write!(out, ", ");
         }
         match &arg.ty {
-            Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::repr("),
+            Type::Str(_) => write!(out, "::rust::Str::Repr("),
             Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr("),
             ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
             _ => {}
@@ -932,7 +878,7 @@ fn write_rust_function_shim_impl(
     write!(out, ")");
     if !indirect_return {
         if let Some(ret) = &sig.ret {
-            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) = ret {
+            if let Type::RustBox(_) | Type::UniquePtr(_) = ret {
                 write!(out, ")");
             }
         }
@@ -940,7 +886,7 @@ fn write_rust_function_shim_impl(
     writeln!(out, ";");
     if sig.throws {
         writeln!(out, "  if (error$.ptr) {{");
-        writeln!(out, "    throw ::rust::impl<::rust::Error>::error(error$);");
+        writeln!(out, "    throw ::rust::Error(error$);");
         writeln!(out, "  }}");
     }
     if indirect_return {
@@ -976,6 +922,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
             write_type(out, &ty.inner);
             write!(out, " *");
         }
+        Type::Str(_) => write!(out, "::rust::Str::Repr"),
         Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr"),
         _ => write_type(out, ty),
     }
@@ -1003,7 +950,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
             write_type(out, &ty.inner);
             write!(out, " *");
         }
-        Some(Type::Str(_)) => write!(out, "::rust::repr::PtrLen "),
+        Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "),
         Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice<uint8_t>::Repr "),
         Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
         _ => write_return_type(out, ty),
@@ -1016,7 +963,7 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) {
             write_type_space(out, &ty.inner);
             write!(out, "*");
         }
-        Type::Str(_) => write!(out, "::rust::repr::PtrLen "),
+        Type::Str(_) => write!(out, "::rust::Str::Repr "),
         Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr "),
         _ => write_type_space(out, &arg.ty),
     }
diff --git a/include/cxx.h b/include/cxx.h
index 220f45f8..0373a386 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -19,11 +19,6 @@ inline namespace cxxbridge05 {
 
 struct unsafe_bitcopy_t;
 
-namespace {
-template <typename T>
-class impl;
-}
-
 #ifndef CXXBRIDGE05_RUST_STRING
 #define CXXBRIDGE05_RUST_STRING
 class String final {
@@ -57,15 +52,18 @@ private:
 #endif // CXXBRIDGE05_RUST_STRING
 
 #ifndef CXXBRIDGE05_RUST_STR
+#define CXXBRIDGE05_RUST_STR
 class Str final {
 public:
   Str() noexcept;
+  Str(const Str &) noexcept;
+
   Str(const std::string &);
   Str(const char *);
   Str(const char *, size_t);
   Str(std::string &&) = delete;
 
-  Str &operator=(const Str &) noexcept = default;
+  Str &operator=(Str) noexcept;
 
   explicit operator std::string() const;
 
@@ -74,16 +72,19 @@ public:
   size_t size() const noexcept;
   size_t length() const noexcept;
 
-  // Important in order for System V ABI to pass in registers.
-  Str(const Str &) noexcept = default;
-  ~Str() noexcept = default;
-
-private:
-  friend impl<Str>;
+  // Repr is PRIVATE; must not be used other than by our generated code.
+  //
   // Not necessarily ABI compatible with &str. Codegen will translate to
   // cxx::rust_str::RustStr which matches this layout.
-  const char *ptr;
-  size_t len;
+  struct Repr {
+    const char *ptr;
+    size_t len;
+  };
+  Str(Repr) noexcept;
+  explicit operator Repr() noexcept;
+
+private:
+  Repr repr;
 };
 #endif // CXXBRIDGE05_RUST_STR
 
@@ -92,29 +93,26 @@ template <typename T>
 class Slice final {
 public:
   Slice() noexcept;
+  Slice(const Slice<T> &) noexcept;
   Slice(const T *, size_t count) noexcept;
 
-  Slice &operator=(const Slice<T> &) noexcept = default;
+  Slice &operator=(Slice<T>) noexcept;
 
   const T *data() const noexcept;
   size_t size() const noexcept;
   size_t length() const noexcept;
 
-  // Important in order for System V ABI to pass in registers.
-  Slice(const Slice<T> &) noexcept = default;
-  ~Slice() noexcept = default;
-
   // Repr is PRIVATE; must not be used other than by our generated code.
   //
   // At present this class is only used for &[u8] slices.
   // Not necessarily ABI compatible with &[u8]. Codegen will translate to
   // cxx::rust_sliceu8::RustSliceU8 which matches this layout.
-  struct Repr final {
+  struct Repr {
     const T *ptr;
     size_t len;
   };
   Slice(Repr) noexcept;
-  explicit operator Repr() const noexcept;
+  explicit operator Repr() noexcept;
 
 private:
   Repr repr;
@@ -184,7 +182,7 @@ public:
   const T &front() const;
   const T &back() const;
 
-  class const_iterator final {
+  class const_iterator {
   public:
     using difference_type = ptrdiff_t;
     using value_type = typename std::add_const<T>::type;
@@ -228,7 +226,7 @@ template <typename Signature, bool Throws = false>
 class Fn;
 
 template <typename Ret, typename... Args, bool Throws>
-class Fn<Ret(Args...), Throws> final {
+class Fn<Ret(Args...), Throws> {
 public:
   Ret operator()(Args... args) const noexcept(!Throws);
   Fn operator*() const noexcept;
@@ -248,18 +246,12 @@ class Error final : public std::exception {
 public:
   Error(const Error &);
   Error(Error &&) noexcept;
+  Error(Str::Repr) noexcept;
   ~Error() noexcept;
-
-  Error &operator=(const Error &);
-  Error &operator=(Error &&) noexcept;
-
   const char *what() const noexcept override;
 
 private:
-  Error() noexcept = default;
-  friend impl<Error>;
-  const char *msg;
-  size_t len;
+  Str::Repr msg;
 };
 #endif // CXXBRIDGE05_RUST_ERROR
 
@@ -313,30 +305,30 @@ Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
 
 #ifndef CXXBRIDGE05_RUST_BITCOPY
 #define CXXBRIDGE05_RUST_BITCOPY
-struct unsafe_bitcopy_t final {
+struct unsafe_bitcopy_t {
   explicit unsafe_bitcopy_t() = default;
 };
 
 constexpr unsafe_bitcopy_t unsafe_bitcopy{};
 #endif // CXXBRIDGE05_RUST_BITCOPY
 
-#ifndef CXXBRIDGE05_RUST_STR
-#define CXXBRIDGE05_RUST_STR
-inline const char *Str::data() const noexcept { return this->ptr; }
-
-inline size_t Str::size() const noexcept { return this->len; }
-
-inline size_t Str::length() const noexcept { return this->len; }
-#endif // CXXBRIDGE05_RUST_STR
-
 #ifndef CXXBRIDGE05_RUST_SLICE
 #define CXXBRIDGE05_RUST_SLICE
 template <typename T>
 Slice<T>::Slice() noexcept : repr(Repr{reinterpret_cast<const T *>(this), 0}) {}
 
+template <typename T>
+Slice<T>::Slice(const Slice<T> &) noexcept = default;
+
 template <typename T>
 Slice<T>::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {}
 
+template <typename T>
+Slice<T> &Slice<T>::operator=(Slice<T> other) noexcept {
+  this->repr = other.repr;
+  return *this;
+}
+
 template <typename T>
 const T *Slice<T>::data() const noexcept {
   return this->repr.ptr;
@@ -356,7 +348,7 @@ template <typename T>
 Slice<T>::Slice(Repr repr_) noexcept : repr(repr_) {}
 
 template <typename T>
-Slice<T>::operator Repr() const noexcept {
+Slice<T>::operator Repr() noexcept {
   return this->repr;
 }
 #endif // CXXBRIDGE05_RUST_SLICE
diff --git a/src/cxx.cc b/src/cxx.cc
index 5877b358..a423df63 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -5,7 +5,6 @@
 #include <iostream>
 #include <memory>
 #include <stdexcept>
-#include <type_traits>
 #include <vector>
 
 extern "C" {
@@ -116,88 +115,82 @@ std::ostream &operator<<(std::ostream &os, const String &s) {
   return os;
 }
 
-Str::Str() noexcept : ptr(reinterpret_cast<const char *>(1)), len(0) {}
+Str::Str() noexcept : repr(Repr{reinterpret_cast<const char *>(1), 0}) {}
 
-static void initStr(const char *ptr, size_t len) {
-  if (!cxxbridge05$str$valid(ptr, len)) {
+Str::Str(const Str &) noexcept = default;
+
+static void initStr(Str::Repr repr) {
+  if (!cxxbridge05$str$valid(repr.ptr, repr.len)) {
     panic<std::invalid_argument>("data for rust::Str is not utf-8");
   }
 }
 
-Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) {
-  initStr(this->ptr, this->len);
+Str::Str(const std::string &s) : repr(Repr{s.data(), s.length()}) {
+  initStr(this->repr);
 }
 
-Str::Str(const char *s) : ptr(s), len(std::strlen(s)) {
+Str::Str(const char *s) : repr(Repr{s, std::strlen(s)}) {
   assert(s != nullptr);
-  initStr(this->ptr, this->len);
+  initStr(this->repr);
 }
 
 Str::Str(const char *s, size_t len)
-    : ptr(s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s),
-      len(len) {
+    : repr(
+          Repr{s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
+               len}) {
   assert(s != nullptr || len == 0);
-  initStr(this->ptr, this->len);
+  initStr(this->repr);
+}
+
+Str &Str::operator=(Str other) noexcept {
+  this->repr = other.repr;
+  return *this;
 }
 
 Str::operator std::string() const {
   return std::string(this->data(), this->size());
 }
 
+const char *Str::data() const noexcept { return this->repr.ptr; }
+
+size_t Str::size() const noexcept { return this->repr.len; }
+
+size_t Str::length() const noexcept { return this->repr.len; }
+
+Str::Str(Repr repr_) noexcept : repr(repr_) {}
+
+Str::operator Repr() noexcept { return this->repr; }
+
 std::ostream &operator<<(std::ostream &os, const Str &s) {
   os.write(s.data(), s.size());
   return os;
 }
 
-static_assert(std::is_trivially_copy_constructible<Str>::value,
-              "trivial Str(const Str &)");
-static_assert(std::is_trivially_copy_assignable<Str>::value,
-              "trivial operator=(const Str &)");
-static_assert(std::is_trivially_destructible<Str>::value, "trivial ~Str()");
-
 extern "C" {
 const char *cxxbridge05$error(const char *ptr, size_t len) {
   char *copy = new char[len];
-  std::strncpy(copy, ptr, len);
+  strncpy(copy, ptr, len);
   return copy;
 }
 } // extern "C"
 
-Error::Error(const Error &other)
-    : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)),
-      len(other.len) {}
+Error::Error(Str::Repr msg) noexcept : msg(msg) {}
 
-Error::Error(Error &&other) noexcept
-    : std::exception(std::move(other)), msg(other.msg), len(other.len) {
-  other.msg = nullptr;
-  other.len = 0;
+Error::Error(const Error &other) {
+  this->msg.ptr = cxxbridge05$error(other.msg.ptr, other.msg.len);
+  this->msg.len = other.msg.len;
 }
 
-Error::~Error() noexcept { delete[] this->msg; }
-
-Error &Error::operator=(const Error &other) {
-  if (this != &other) {
-    std::exception::operator=(other);
-    delete[] this->msg;
-    this->msg = nullptr;
-    this->msg = cxxbridge05$error(other.msg, other.len);
-    this->len = other.len;
-  }
-  return *this;
+Error::Error(Error &&other) noexcept {
+  delete[] this->msg.ptr;
+  this->msg = other.msg;
+  other.msg.ptr = nullptr;
+  other.msg.len = 0;
 }
 
-Error &Error::operator=(Error &&other) noexcept {
-  if (this != &other) {
-    std::exception::operator=(std::move(other));
-    this->msg = other.msg;
-    this->len = other.len;
-    other.msg = nullptr;
-    other.len = 0;
-  }
-  return *this;
-}
+Error::~Error() noexcept { delete[] this->msg.ptr; }
 
-const char *Error::what() const noexcept { return this->msg; }
+const char *Error::what() const noexcept { return this->msg.ptr; }
 
 } // namespace cxxbridge05
 } // namespace rust


Test patch:
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
index 1818af4c..527d7090 100644
--- a/tests/cxx_gen.rs
+++ b/tests/cxx_gen.rs
@@ -18,7 +18,7 @@ fn test_extern_c_function() {
     let output = str::from_utf8(&generated.implementation).unwrap();
     // To avoid continual breakage we won't test every byte.
     // Let's look for the major features.
-    assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+    assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)"));
 }
 
 #[test]
@@ -28,5 +28,5 @@ fn test_impl_annotation() {
     let source = BRIDGE0.parse().unwrap();
     let generated = generate_header_and_cc(source, &opt).unwrap();
     let output = str::from_utf8(&generated.implementation).unwrap();
-    assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+    assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::Str::Repr foo)"));
 }
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index fee5bcb1..c79aff3d 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -101,6 +101,16 @@ pub mod ffi {
         z: usize,
     }
 
+    #[namespace = "first"]
+    struct First {
+        second: Box<Second>,
+    }
+
+    #[namespace = "second"]
+    struct Second {
+        i: i32,
+    }
+
     extern "C" {
         include!("tests/ffi/tests.h");