Support fieldless `recursively_pinned` structs and enums.

(In the case of structs, that's empty structs -- in the case of enums, it's C-like enums.)

PiperOrigin-RevId: 447449578
diff --git a/rs_bindings_from_cc/support/ctor_proc_macros.rs b/rs_bindings_from_cc/support/ctor_proc_macros.rs
index 36b44d4..5a7bd0f 100644
--- a/rs_bindings_from_cc/support/ctor_proc_macros.rs
+++ b/rs_bindings_from_cc/support/ctor_proc_macros.rs
@@ -98,16 +98,29 @@
     projected.ident = projected_ident(&projected.ident);
     let projected_ident = &projected.ident;
 
-    let lifetime = syn::Lifetime::new("'proj", Span::call_site());
     assert_eq!(
         projected.generics.params.len(),
         0,
         "pin projection is currently not implemented for generic structs"
     );
-    projected
-        .generics
-        .params
-        .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(lifetime.clone())));
+
+    let is_fieldless = match &projected.data {
+        syn::Data::Struct(data) => data.fields.is_empty(),
+        syn::Data::Enum(e) => e.variants.iter().all(|variant| variant.fields.is_empty()),
+        syn::Data::Union(_) => unimplemented!(),
+    };
+
+    let lifetime;
+    if is_fieldless {
+        lifetime = quote! {};
+    } else {
+        let syn_lifetime = syn::Lifetime::new("'proj", Span::call_site());
+        projected
+            .generics
+            .params
+            .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(syn_lifetime.clone())));
+        lifetime = quote! {#syn_lifetime};
+    }
 
     let project_field = |field: &mut syn::Field| {
         field.attrs.clear();
@@ -219,7 +232,9 @@
         #projected_impl
 
         impl #name {
-            fn project(self: ::std::pin::Pin<&mut Self>) -> #projected_ident<'_> {
+            #[must_use]
+            #[inline(always)]
+            fn project(self: ::std::pin::Pin<&mut Self>) -> #projected_ident {
                 #projected_ident::new(self)
             }
         }
diff --git a/rs_bindings_from_cc/support/ctor_proc_macros_test.rs b/rs_bindings_from_cc/support/ctor_proc_macros_test.rs
index 48c9e20..a469658 100644
--- a/rs_bindings_from_cc/support/ctor_proc_macros_test.rs
+++ b/rs_bindings_from_cc/support/ctor_proc_macros_test.rs
@@ -48,6 +48,40 @@
 }
 
 #[test]
+fn test_recursively_pinned_unit_struct() {
+    #[::ctor::recursively_pinned]
+    struct S;
+    let _ = Box::pin(S).as_mut().project();
+    assert_eq!(::std::mem::size_of::<::ctor::projected!(S)>(), 0);
+}
+
+#[test]
+fn test_recursively_pinned_fieldless_struct() {
+    #[::ctor::recursively_pinned]
+    struct S {}
+    let _ = Box::pin(S {}).as_mut().project();
+    assert_eq!(::std::mem::size_of::<::ctor::projected!(S)>(), 0);
+}
+
+#[test]
+fn test_recursively_pinned_fieldless_tuple_struct() {
+    #[::ctor::recursively_pinned]
+    struct S();
+    let _ = Box::pin(S()).as_mut().project();
+    assert_eq!(::std::mem::size_of::<::ctor::projected!(S)>(), 0);
+}
+
+#[test]
+fn test_recursively_pinned_fieldless_enum() {
+    #[::ctor::recursively_pinned]
+    enum E {
+        A,
+    }
+    let <::ctor::projected!(E)>::A = Box::pin(E::A).as_mut().project();
+    assert_eq!(::std::mem::size_of::<::ctor::projected!(E)>(), 0);
+}
+
+#[test]
 fn test_recursively_pinned_struct() {
     #[::ctor::recursively_pinned]
     struct S {