Prevent double destroy in InlinedVector swap on exception unwinding

PiperOrigin-RevId: 946273899
Change-Id: I8d7e8881f47e09ba6201daba6c04f512cccf3df9
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 67b4368..6ed9886 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -2313,4 +2313,53 @@
   EXPECT_THAT(v, IsEmpty());
 }
 
+#ifdef ABSL_HAVE_EXCEPTIONS
+struct ThrowOnMove {
+  static constexpr uint32_t kAlive = 0xA11FE123;
+  static constexpr uint32_t kDestroyed = 0xDEADBEEF;
+
+  explicit ThrowOnMove(int* count) : alive_count(count), sentinel(kAlive) {
+    if (alive_count) ++(*alive_count);
+  }
+  ThrowOnMove(const ThrowOnMove&) = delete;
+  ThrowOnMove& operator=(const ThrowOnMove&) = delete;
+  ThrowOnMove(ThrowOnMove&& other)
+      : alive_count(other.alive_count), sentinel(kAlive) {
+    if (other.should_throw) throw std::runtime_error("ThrowOnMove");
+    if (alive_count) ++(*alive_count);
+  }
+  ~ThrowOnMove() {
+    EXPECT_EQ(sentinel, kAlive)
+        << "Double destroy detected: destructor called twice on memory slot!";
+    sentinel = kDestroyed;
+    if (alive_count) {
+      EXPECT_GT(*alive_count, 0)
+          << "More destructors called than constructors!";
+      --(*alive_count);
+    }
+  }
+
+  int* alive_count = nullptr;
+  uint32_t sentinel = kDestroyed;
+  bool should_throw = false;
+};
+
+TEST(InlinedVectorTest, SwapExceptionSafety) {
+  int alive_count = 0;
+  try {
+    absl::InlinedVector<ThrowOnMove, 2> a;
+    a.emplace_back(&alive_count);
+    absl::InlinedVector<ThrowOnMove, 2> b;
+    b.emplace_back(&alive_count);
+    b[0].should_throw = true;
+    a.swap(b);
+    FAIL() << "Expected swap to throw std::runtime_error";
+  } catch (const std::runtime_error&) {
+    // Expected exception from throwing move-constructor inside swap.
+  }
+  EXPECT_EQ(alive_count, 0)
+      << "Mismatch between constructor and destructor calls!";
+}
+#endif
+
 }  // anonymous namespace
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index 4e9b87e..e6491ba 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -1038,10 +1038,22 @@
     ValueType<A> tmp(std::move(*a));
 
     AllocatorTraits<A>::destroy(allocator_a, a);
-    AllocatorTraits<A>::construct(allocator_b, a, std::move(*b));
+    ABSL_INTERNAL_TRY {
+      AllocatorTraits<A>::construct(allocator_b, a, std::move(*b));
+    }
+    ABSL_INTERNAL_CATCH_ANY {
+      AllocatorTraits<A>::construct(allocator_a, a, std::move(tmp));
+      ABSL_INTERNAL_RETHROW;
+    }
 
     AllocatorTraits<A>::destroy(allocator_b, b);
-    AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp));
+    ABSL_INTERNAL_TRY {
+      AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp));
+    }
+    ABSL_INTERNAL_CATCH_ANY {
+      AllocatorTraits<A>::construct(allocator_b, b, std::move(*a));
+      ABSL_INTERNAL_RETHROW;
+    }
   }
 }