@@ -545,6 +545,7 @@ enum InvalidCapacity : size_t {
545545 kAboveMaxValidCapacity = ~size_t {} - 100 ,
546546 kReentrance ,
547547 kDestroyed ,
548+ kMovedFrom ,
548549};
549550
550551// Returns a pointer to a control byte group that can be used by empty tables.
@@ -2402,6 +2403,10 @@ class raw_hash_set {
24022403 alignof (slot_type) <= alignof (HeapOrSoo);
24032404 }
24042405
2406+ constexpr static size_t DefaultCapacity () {
2407+ return SooEnabled () ? SooCapacity () : 0 ;
2408+ }
2409+
24052410 // Whether `size` fits in the SOO capacity of this table.
24062411 bool fits_in_soo (size_t size) const {
24072412 return SooEnabled () && size <= SooCapacity ();
@@ -2639,7 +2644,7 @@ class raw_hash_set {
26392644 const allocator_type& alloc = allocator_type())
26402645 : settings_(CommonFields::CreateDefault<SooEnabled()>(), hash, eq,
26412646 alloc) {
2642- if (bucket_count > ( SooEnabled () ? SooCapacity () : 0 )) {
2647+ if (bucket_count > DefaultCapacity ( )) {
26432648 resize (NormalizeCapacity (bucket_count));
26442649 }
26452650 }
@@ -2822,15 +2827,15 @@ class raw_hash_set {
28222827 transfer (soo_slot (), that.soo_slot ());
28232828 }
28242829 that.common () = CommonFields::CreateDefault<SooEnabled ()>();
2825- maybe_increment_generation_or_rehash_on_move ( );
2830+ annotate_for_bug_detection_on_move (that );
28262831 }
28272832
28282833 raw_hash_set (raw_hash_set&& that, const allocator_type& a)
28292834 : settings_(CommonFields::CreateDefault<SooEnabled()>(), that.hash_ref(),
28302835 that.eq_ref(), a) {
28312836 if (a == that.alloc_ref ()) {
28322837 swap_common (that);
2833- maybe_increment_generation_or_rehash_on_move ( );
2838+ annotate_for_bug_detection_on_move (that );
28342839 } else {
28352840 move_elements_allocs_unequal (std::move (that));
28362841 }
@@ -2896,15 +2901,19 @@ class raw_hash_set {
28962901 size_t size () const { return common ().size (); }
28972902 size_t capacity () const {
28982903 const size_t cap = common ().capacity ();
2899- // Compiler complains when using functions in assume so use local variables .
2900- ABSL_ATTRIBUTE_UNUSED static constexpr bool kEnabled = SooEnabled ();
2901- ABSL_ATTRIBUTE_UNUSED static constexpr size_t kCapacity = SooCapacity ();
2902- ABSL_ASSUME (! kEnabled || cap >= kCapacity );
2904+ // Compiler complains when using functions in ASSUME so use local variable .
2905+ ABSL_ATTRIBUTE_UNUSED static constexpr size_t kDefaultCapacity =
2906+ DefaultCapacity ();
2907+ ABSL_ASSUME (cap >= kDefaultCapacity );
29032908 return cap;
29042909 }
29052910 size_t max_size () const { return (std::numeric_limits<size_t >::max)(); }
29062911
29072912 ABSL_ATTRIBUTE_REINITIALIZES void clear () {
2913+ if (SwisstableGenerationsEnabled () &&
2914+ capacity () == InvalidCapacity::kMovedFrom ) {
2915+ common ().set_capacity (DefaultCapacity ());
2916+ }
29082917 AssertNotDebugCapacity ();
29092918 // Iterating over this container is O(bucket_count()). When bucket_count()
29102919 // is much greater than size(), iteration becomes prohibitively expensive.
@@ -3586,6 +3595,10 @@ class raw_hash_set {
35863595 }
35873596
35883597 inline void destructor_impl () {
3598+ if (SwisstableGenerationsEnabled () &&
3599+ capacity () == InvalidCapacity::kMovedFrom ) {
3600+ return ;
3601+ }
35893602 if (capacity () == 0 ) return ;
35903603 if (is_soo ()) {
35913604 if (!empty ()) {
@@ -3759,8 +3772,16 @@ class raw_hash_set {
37593772 move_common (that_is_full_soo, that.alloc_ref (), common (), std::move (tmp));
37603773 }
37613774
3762- void maybe_increment_generation_or_rehash_on_move () {
3763- if (!SwisstableGenerationsEnabled () || capacity () == 0 || is_soo ()) {
3775+ void annotate_for_bug_detection_on_move (
3776+ ABSL_ATTRIBUTE_UNUSED raw_hash_set& that) {
3777+ // We only enable moved-from validation when generations are enabled (rather
3778+ // than using NDEBUG) to avoid issues in which NDEBUG is enabled in some
3779+ // translation units but not in others.
3780+ if (SwisstableGenerationsEnabled () && this != &that) {
3781+ that.common ().set_capacity (InvalidCapacity::kMovedFrom );
3782+ }
3783+ if (!SwisstableGenerationsEnabled () || capacity () == DefaultCapacity () ||
3784+ capacity () > kAboveMaxValidCapacity ) {
37643785 return ;
37653786 }
37663787 common ().increment_generation ();
@@ -3782,7 +3803,7 @@ class raw_hash_set {
37823803 CopyAlloc (alloc_ref (), that.alloc_ref (),
37833804 std::integral_constant<bool , propagate_alloc>());
37843805 that.common () = CommonFields::CreateDefault<SooEnabled ()>();
3785- maybe_increment_generation_or_rehash_on_move ( );
3806+ annotate_for_bug_detection_on_move (that );
37863807 return *this ;
37873808 }
37883809
@@ -3796,7 +3817,7 @@ class raw_hash_set {
37963817 }
37973818 if (!that.is_soo ()) that.dealloc ();
37983819 that.common () = CommonFields::CreateDefault<SooEnabled ()>();
3799- maybe_increment_generation_or_rehash_on_move ( );
3820+ annotate_for_bug_detection_on_move (that );
38003821 return *this ;
38013822 }
38023823
@@ -3875,27 +3896,30 @@ class raw_hash_set {
38753896 // Asserts for correctness that we run on find/find_or_prepare_insert.
38763897 template <class K >
38773898 void AssertOnFind (ABSL_ATTRIBUTE_UNUSED const K& key) {
3878- #ifdef NDEBUG
3879- return ;
3880- #endif
38813899 AssertHashEqConsistent (key);
38823900 AssertNotDebugCapacity ();
38833901 }
38843902
38853903 // Asserts that the capacity is not a sentinel invalid value.
3886- // TODO(b/296061262): also add asserts for moved-from state.
38873904 void AssertNotDebugCapacity () const {
38883905 assert (capacity () != InvalidCapacity::kReentrance &&
3889- " reentrant container access during element construction/destruction "
3906+ " Reentrant container access during element construction/destruction "
38903907 " is not allowed." );
38913908 assert (capacity () != InvalidCapacity::kDestroyed &&
3892- " use of destroyed hash table." );
3909+ " Use of destroyed hash table." );
3910+ if (SwisstableGenerationsEnabled () &&
3911+ ABSL_PREDICT_FALSE (capacity () == InvalidCapacity::kMovedFrom )) {
3912+ ABSL_RAW_LOG (FATAL, " Use of moved-from hash table." );
3913+ }
38933914 }
38943915
38953916 // Asserts that hash and equal functors provided by the user are consistent,
38963917 // meaning that `eq(k1, k2)` implies `hash(k1)==hash(k2)`.
38973918 template <class K >
38983919 void AssertHashEqConsistent (const K& key) {
3920+ #ifdef NDEBUG
3921+ return ;
3922+ #endif
38993923 // If the hash/eq functors are known to be consistent, then skip validation.
39003924 if (std::is_same<hasher, absl::container_internal::StringHash>::value &&
39013925 std::is_same<key_equal, absl::container_internal::StringEq>::value) {
0 commit comments