Truncate absl::InlinedVector prior to moving, in case moves throw an exception

PiperOrigin-RevId: 947105228
Change-Id: I697670634bf8c9c88f10a85cf18e4c9ce2deb268
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 744d3d9..2694a5d 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -948,6 +948,12 @@
         storage_.GetAllocator(), data(), size());
     storage_.DeallocateIfAllocated();
 
+    if constexpr (!std::is_nothrow_move_constructible_v<value_type>) {
+      // Reset the size to zero before moving to avoid leaking freed memory if
+      // an exception is thrown.
+      storage_.SetInlinedSize(0);
+    }
+
     IteratorValueAdapter<A, MoveIterator<A>> other_values(
         MoveIterator<A>(other.storage_.GetInlinedData()));
     inlined_vector_internal::ConstructElements<A>(
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 6ed9886..c9af35d 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -2360,6 +2360,27 @@
   EXPECT_EQ(alive_count, 0)
       << "Mismatch between constructor and destructor calls!";
 }
+
+// Ensures that an exception thrown during move construction doesn't leave the
+// inlined vector in a bad state. Needs ASAN for reliable detection.
+TEST(InlinedVectorTest, TruncatesBeforeThrowingMoveConstruction) {
+  using Vec = absl::InlinedVector<ThrowOnMove, 1>;
+  int count = 0;
+  {
+    Vec dest;
+    dest.reserve(dest.capacity() + 1);  // force heap allocation
+    dest.emplace_back(&count);
+
+    Vec src;
+    src.emplace_back(&count).should_throw = true;
+
+    try {
+      dest = std::move(src);
+    } catch (const std::runtime_error&) {
+    }
+  }
+  EXPECT_EQ(count, 0);
+}
 #endif
 
 }  // anonymous namespace