Repository: google/autocxx (Rust)

Issue:
No `safety!` results in `unsafe fn drop`, which doesn't compile
## Expected Behavior
Always generate safe `drop` implementations, as required by `Drop`.

## Actual Behavior
With `safety!(unsafe)` or `safety!(unsafe_ffi)`, it's fine. Without any `safety!`, `drop` ends up being marked `unsafe`, which doesn't compile.

## Steps to Reproduce the Problem
See #789 for a failing test.

## Specifications

- Version: 49665846d6219d149fe11f9a39e9e4fa5001ac26
- Platform: Linux


Discussion:
I really appreciate the addition of the test, thanks! I'm hoping this one is an easy fix, we'll see.
Thanks!



Patch:
diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs
index a8e8a40db..8e7879832 100644
--- a/engine/src/conversion/analysis/fun/mod.rs
+++ b/engine/src/conversion/analysis/fun/mod.rs
@@ -143,7 +143,7 @@ pub(crate) enum RustRenameStrategy {
 #[derive(Clone)]
 pub(crate) enum UnsafetyNeeded {
     None,
-    JustReceiver,
+    JustBridge,
     Always,
 }
 
@@ -355,25 +355,29 @@ impl<'a> FnAnalyzer<'a> {
         param_details: &[ArgumentAnalysis],
         kind: &FnKind,
     ) -> UnsafetyNeeded {
-        if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe {
-            UnsafetyNeeded::Always
-        } else if param_details
-            .iter()
-            .any(|pd| pd.self_type.is_none() && pd.requires_unsafe)
-        {
-            UnsafetyNeeded::Always
-        } else if matches!(
-            kind,
+        let any_non_self_param_needs_unsafe = || -> bool {
+            param_details
+                .iter()
+                .any(|pd| pd.self_type.is_none() && pd.requires_unsafe)
+        };
+        let any_param_needs_unsafe =
+            || -> bool { param_details.iter().any(|pd| pd.requires_unsafe) };
+        match kind {
+            // Trait unsafety must always correspond to the norms for the
+            // trait we're implementing.
             FnKind::TraitMethod {
                 kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor,
                 ..
+            } => UnsafetyNeeded::Always,
+            FnKind::TraitMethod { .. } if any_param_needs_unsafe() => UnsafetyNeeded::JustBridge,
+            FnKind::TraitMethod { .. } => UnsafetyNeeded::None,
+            _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe
+                || any_non_self_param_needs_unsafe() =>
+            {
+                UnsafetyNeeded::Always
             }
-        ) {
-            UnsafetyNeeded::Always
-        } else if param_details.iter().any(|pd| pd.requires_unsafe) {
-            UnsafetyNeeded::JustReceiver
-        } else {
-            UnsafetyNeeded::None
+            _ if any_param_needs_unsafe() => UnsafetyNeeded::JustBridge,
+            _ => UnsafetyNeeded::None,
         }
     }
 


Test patch:
diff --git a/integration-tests/src/tests.rs b/integration-tests/src/tests.rs
index 231dd525b..4fb9f3eb0 100644
--- a/integration-tests/src/tests.rs
+++ b/integration-tests/src/tests.rs
@@ -2286,6 +2286,34 @@ fn test_destructor() {
     run_test(cxx, hdr, rs, &["WithDtor", "make_with_dtor"], &[]);
 }
 
+// Even without a `safety!`, we still need to generate a safe `fn drop`.
+#[test]
+fn test_destructor_no_safety() {
+    let hdr = indoc! {"
+        struct WithDtor {
+            ~WithDtor();
+        };
+    "};
+    let cxx = indoc! {"
+        WithDtor::~WithDtor() {}
+    "};
+    let hexathorpe = Token![#](Span::call_site());
+    let unexpanded_rust = |hdr: &str| {
+        quote! {
+            use autocxx::prelude::*;
+
+            include_cpp!(
+                #hexathorpe include #hdr
+                generate!("WithDtor")
+            );
+
+            fn main() {}
+        }
+    };
+
+    do_run_test_manual(cxx, hdr, unexpanded_rust, None, None).unwrap();
+}
+
 #[test]
 fn test_static_func() {
     let hdr = indoc! {"