APInt: Make self-move-assignment a no-op to fix stage3 clang-cl
authorReid Kleckner <reid@kleckner.net>
Tue, 12 Aug 2014 22:01:39 +0000 (22:01 +0000)
committerReid Kleckner <reid@kleckner.net>
Tue, 12 Aug 2014 22:01:39 +0000 (22:01 +0000)
It's not clear what the semantics of a self-move should be.  The
consensus appears to be that a self-move should leave the object in a
moved-from state, which is what our existing move assignment operator
does.

However, the MSVC 2013 STL will perform self-moves in some cases.  In
particular, when doing a std::stable_sort of an already sorted APSInt
vector of an appropriate size, one of the merge steps will self-move
half of the elements.

We don't notice this when building with MSVC, because MSVC will not
synthesize the move assignment operator for APSInt.  Presumably MSVC
does this because APInt, the base class, has user-declared special
members that implicitly delete move special members.  Instead, MSVC
selects the copy-assign operator, which defends against self-assignment.
Clang, on the other hand, selects the move-assign operator, and we get
garbage APInts.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215478 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/APInt.h
unittests/ADT/APIntTest.cpp

index aa3c3f67ec1043529aa25f30062910942e3e8c45..f815628f30c71dbc3cec909a979b9ab5ca2ffc4f 100644 (file)
@@ -656,13 +656,22 @@ public:
 
   /// @brief Move assignment operator.
   APInt &operator=(APInt &&that) {
-    if (!isSingleWord())
+    if (!isSingleWord()) {
+      // The MSVC STL shipped in 2013 requires that self move assignment be a
+      // no-op.  Otherwise algorithms like stable_sort will produce answers
+      // where half of the output is left in a moved-from state.
+      if (this == &that)
+        return *this;
       delete[] pVal;
+    }
 
-    BitWidth = that.BitWidth;
     VAL = that.VAL;
 
+    // If 'this == &that', avoid zeroing our own bitwidth by storing to 'that'
+    // first.
+    unsigned ThatBitWidth = that.BitWidth;
     that.BitWidth = 0;
+    BitWidth = ThatBitWidth;
 
     return *this;
   }
index 19c47ab13bf9c321c511e2a3a412a80e88c79635..7da3fb25548a6b1dcbc781b752065c8f7631c7c7 100644 (file)
@@ -678,4 +678,21 @@ TEST(APIntTest, nearestLogBase2) {
   EXPECT_EQ(A9.nearestLogBase2(), UINT32_MAX);
 }
 
+TEST(APIntTest, SelfMoveAssignment) {
+  APInt X(32, 0xdeadbeef);
+  X = std::move(X);
+  EXPECT_EQ(32, X.getBitWidth());
+  EXPECT_EQ(0xdeadbeefULL, X.getLimitedValue());
+
+  uint64_t Bits[] = {0xdeadbeefdeadbeefULL, 0xdeadbeefdeadbeefULL};
+  APInt Y(128, Bits);
+  Y = std::move(Y);
+  EXPECT_EQ(128, Y.getBitWidth());
+  EXPECT_EQ(~0ULL, Y.getLimitedValue());
+  const uint64_t *Raw = Y.getRawData();
+  EXPECT_EQ(2, Y.getNumWords());
+  EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[0]);
+  EXPECT_EQ(0xdeadbeefdeadbeefULL, Raw[1]);
+}
+
 }