[PATCH] D93779: ADT: Fix reference invalidation in SmallVector::push_back and single-element insert

Duncan P. N. Exon Smith via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 11 13:11:18 PST 2021


dexonsmith marked 4 inline comments as done.
dexonsmith added a comment.

Thanks for the review! (Just coming back to this now after the holidays.)



================
Comment at: llvm/include/llvm/ADT/SmallVector.h:711-713
+    static_assert(!TakesParamByValue || std::is_same<ArgType, T>::value,
+                  "ArgType must be 'T' when taking by value!");
+    if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end()))
----------------
I added a static assert here to confirm we have a copy, since the logic relies on that.


================
Comment at: llvm/include/llvm/ADT/SmallVector.h:480-490
+  /// Reserve enough space to add one element, and return the updated element
+  /// pointer in case it was a reference to the storage.
+  T *reserveForAndGetAddress(T &Elt) {
+    return this->reserveForAndGetAddressImpl(this, Elt);
+  }
+
+  /// Reserve enough space to add one element, and return the updated element
----------------
dblaikie wrote:
> While I think it's not technically a problem (because the casted-away-const-ness pointer is never dereferenced) - I'd usually implement these the other way around. Have an implementation that uses const pointers only, then the non-const wrapper can cast-to-const, call, cast away const, and it's "clearer" (marginally, perhaps) that it's const correct because only that non-const wrapper has the interesting const handling - rather than the longer distance behavior in this implementation.
Good point; I've updated `...Impl()` to take/return `const`, and both callers now call it directly.


================
Comment at: llvm/include/llvm/ADT/SmallVector.h:693-694
+    size_t Index = I - this->begin();
+    std::remove_reference_t<ArgType> *EltPtr =
+        this->reserveForAndGetAddress(Elt);
+    I = this->begin() + Index;
----------------
dblaikie wrote:
> Is this correct? If ArgType differs from the container's value type, wouldn't this break? (if you insert something that's convertible to the element type, but isn't the actual element type)
> 
> I guess insert_one_maybe_copy does SFINAE, etc to ensure this is always called with the value type? So ArgType is only ever some kind of ref qualified T?
Correct. I added a comment and static assertion above to document this.


================
Comment at: llvm/unittests/ADT/SmallVectorTest.cpp:1088-1089
+  EXPECT_EQ(N, V.back());
+  if (this->template isValueType<Constructable>())
+    EXPECT_EQ(N, V[N - 1]);
 }
----------------
dblaikie wrote:
> I'm not following what this part of the test does - could you walk me through it?
I've rewritten it (I couldn't fully make sense of it either, coming back to it). It now tests:
- The correct value gets copied when pushing back with a reference, growing from small storage.
- The correct value gets copied when pushing back with a reference, growing from large storage.
- The value is actually copied, not moved (the difference is only observable for Constructable). See `PushBackMoved` below, where a `0` is left behind. This seems potentially worth testing in case a `std::forward` is missed somewhere, but i'm open to dropping that part if you think it's already covered well enough above.


================
Comment at: llvm/unittests/ADT/SmallVectorTest.cpp:1172-1176
+  // The logic for reference invalidation when NOT growing is disabled when
+  // TakesParamByValue since it relies on taking a copy. Check that we actually
+  // took a copy.
+  V.insert(V.begin(), V.back());
+  EXPECT_EQ(N, V.front());
----------------
dblaikie wrote:
> I'm not following how this validates that a copy was taken (I'd have thought it'd be unobservable whether a copy was made or the pointer-index-pointer dance was done) - could you walk me through that in a bit more detail?
I've rewritten this in the same way as the `push_back` tests.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D93779/new/

https://reviews.llvm.org/D93779



More information about the llvm-commits mailing list