+2019-07-01 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/90923
+ * hash-map.h (hash_map::put): On insertion invoke element ctor.
+ (hash_map::get_or_insert): Same. Reformat comment.
+ * hash-set.h (hash_set::add): On insertion invoke element ctor.
+ * hash-map-tests.c (test_map_of_type_with_ctor_and_dtor): New.
+ * hash-set-tests.c (test_map_of_type_with_ctor_and_dtor): New.
+ * hash-table.h (hash_table::operator=): Prevent copy assignment.
+ (hash_table::hash_table (const hash_table&)): Use copy ctor
+ instead of assignment to copy elements.
+
2019-07-01 Wilco Dijkstra <wdijkstr@arm.com>
John David Anglin <danglin@gcc.gnu.org>
ASSERT_EQ (1, string_map.elements ());
}
+typedef struct hash_map_test_val_t
+{
+ static int ndefault;
+ static int ncopy;
+ static int nassign;
+ static int ndtor;
+
+ hash_map_test_val_t ()
+ : ptr (&ptr)
+ {
+ ++ndefault;
+ }
+
+ hash_map_test_val_t (const hash_map_test_val_t &)
+ : ptr (&ptr)
+ {
+ ++ncopy;
+ }
+
+ hash_map_test_val_t& operator= (const hash_map_test_val_t &)
+ {
+ ++nassign;
+ return *this;
+ }
+
+ ~hash_map_test_val_t ()
+ {
+ gcc_assert (ptr == &ptr);
+ ++ndtor;
+ }
+
+ void *ptr;
+} val_t;
+
+int val_t::ndefault;
+int val_t::ncopy;
+int val_t::nassign;
+int val_t::ndtor;
+
+static void
+test_map_of_type_with_ctor_and_dtor ()
+{
+ typedef hash_map <void *, val_t> Map;
+
+ {
+ /* Test default ctor. */
+ Map m;
+ (void)&m;
+ }
+
+ ASSERT_TRUE (val_t::ndefault == 0);
+ ASSERT_TRUE (val_t::ncopy == 0);
+ ASSERT_TRUE (val_t::nassign == 0);
+ ASSERT_TRUE (val_t::ndtor == 0);
+
+ {
+ /* Test single insertion. */
+ Map m;
+ void *p = &p;
+ m.get_or_insert (p);
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+ {
+ /* Test copy ctor. */
+ Map m1;
+ void *p = &p;
+ val_t &rv1 = m1.get_or_insert (p);
+
+ int ncopy = val_t::ncopy;
+ int nassign = val_t::nassign;
+
+ Map m2 (m1);
+ val_t *pv2 = m2.get (p);
+
+ ASSERT_TRUE (ncopy + 1 == val_t::ncopy);
+ ASSERT_TRUE (nassign == val_t::nassign);
+
+ ASSERT_TRUE (&rv1 != pv2);
+ ASSERT_TRUE (pv2->ptr == &pv2->ptr);
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+#if 0 /* Avoid testing until bug 90959 is fixed. */
+ {
+ /* Test copy assignment into an empty map. */
+ Map m1;
+ void *p = &p;
+ val_t &rv1 = m1.get_or_insert (p);
+
+ int ncopy = val_t::ncopy;
+ int nassign = val_t::nassign;
+
+ Map m2;
+ m2 = m1;
+ val_t *pv2 = m2.get (p);
+
+ ASSERT_TRUE (ncopy == val_t::ncopy);
+ ASSERT_TRUE (nassign + 1 == val_t::nassign);
+
+ ASSERT_TRUE (&rv1 != pv2);
+ ASSERT_TRUE (pv2->ptr == &pv2->ptr);
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+#endif
+
+ {
+ Map m;
+ void *p = &p, *q = &q;
+ val_t &v1 = m.get_or_insert (p);
+ val_t &v2 = m.get_or_insert (q);
+
+ ASSERT_TRUE (v1.ptr == &v1.ptr && &v2.ptr == v2.ptr);
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+ {
+ Map m;
+ void *p = &p, *q = &q;
+ m.get_or_insert (p);
+ m.remove (p);
+ m.get_or_insert (q);
+ m.remove (q);
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+ }
+}
+
/* Run all of the selftests within this file. */
void
hash_map_tests_c_tests ()
{
test_map_of_strings_to_int ();
+ test_map_of_type_with_ctor_and_dtor ();
}
} // namespace selftest
#ifndef hash_map_h
#define hash_map_h
+/* Class hash_map is a hash-value based container mapping objects of
+ KeyId type to those of the Value type.
+ Both KeyId and Value may be non-trivial (non-POD) types provided
+ a suitabe Traits class. A few default Traits specializations are
+ provided for basic types such as integers, pointers, and std::pair.
+ Inserted elements are value-initialized either to zero for POD types
+ or by invoking their default ctor. Removed elements are destroyed
+ by invoking their dtor. On hash_map destruction all elements are
+ removed. Objects of hash_map type are copy-constructible but not
+ assignable. */
+
template<typename KeyId, typename Value,
- typename Traits>
+ typename Traits /* = simple_hashmap_traits<default_hash_traits<Key>,
+ Value> */>
class GTY((user)) hash_map
{
typedef typename Traits::key_type Key;
{
hash_entry *e = m_table.find_slot_with_hash (k, Traits::hash (k),
INSERT);
- bool existed = !hash_entry::is_empty (*e);
- if (!existed)
- e->m_key = k;
+ bool ins = hash_entry::is_empty (*e);
+ if (ins)
+ {
+ e->m_key = k;
+ new ((void *) &e->m_value) Value (v);
+ }
+ else
+ e->m_value = v;
- e->m_value = v;
- return existed;
+ return !ins;
}
/* if the passed in key is in the map return its value otherwise NULL. */
}
/* Return a reference to the value for the passed in key, creating the entry
- if it doesn't already exist. If existed is not NULL then it is set to false
- if the key was not previously in the map, and true otherwise. */
+ if it doesn't already exist. If existed is not NULL then it is set to
+ false if the key was not previously in the map, and true otherwise. */
Value &get_or_insert (const Key &k, bool *existed = NULL)
{
INSERT);
bool ins = Traits::is_empty (*e);
if (ins)
- e->m_key = k;
+ {
+ e->m_key = k;
+ new ((void *)&e->m_value) Value ();
+ }
if (existed != NULL)
*existed = !ins;
ASSERT_EQ (2, t.elements ());
}
+typedef struct hash_set_test_value_t
+{
+ static int ndefault;
+ static int ncopy;
+ static int nassign;
+ static int ndtor;
+
+ hash_set_test_value_t (int v = 1): pval (&val), val (v)
+ {
+ ++ndefault;
+ }
+
+ hash_set_test_value_t (const hash_set_test_value_t &rhs)
+ : pval (&val), val (rhs.val)
+ {
+ ++ncopy;
+ }
+
+ hash_set_test_value_t& operator= (const hash_set_test_value_t &rhs)
+ {
+ ++nassign;
+ val = rhs.val;
+ return *this;
+ }
+
+ ~hash_set_test_value_t ()
+ {
+ /* Verify that the value hasn't been corrupted. */
+ gcc_assert (*pval > 0);
+ gcc_assert (pval == &val);
+ *pval = -3;
+ ++ndtor;
+ }
+
+ int *pval;
+ int val;
+} val_t;
+
+int val_t::ndefault;
+int val_t::ncopy;
+int val_t::nassign;
+int val_t::ndtor;
+
+struct value_hash_traits: int_hash<int, -1, -2>
+{
+ typedef int_hash<int, -1, -2> base_type;
+ typedef val_t value_type;
+ typedef value_type compare_type;
+
+ static hashval_t hash (const value_type &v)
+ {
+ return base_type::hash (v.val);
+ }
+
+ static bool equal (const value_type &a, const compare_type &b)
+ {
+ return base_type::equal (a.val, b.val);
+ }
+
+ static void mark_deleted (value_type &v)
+ {
+ base_type::mark_deleted (v.val);
+ }
+
+ static void mark_empty (value_type &v)
+ {
+ base_type::mark_empty (v.val);
+ }
+
+ static bool is_deleted (const value_type &v)
+ {
+ return base_type::is_deleted (v.val);
+ }
+
+ static bool is_empty (const value_type &v)
+ {
+ return base_type::is_empty (v.val);
+ }
+
+ static void remove (value_type &v)
+ {
+ v.~value_type ();
+ }
+};
+
+static void
+test_set_of_type_with_ctor_and_dtor ()
+{
+ typedef hash_set <val_t, false, value_hash_traits> Set;
+
+ {
+ Set s;
+ (void)&s;
+ }
+
+ ASSERT_TRUE (val_t::ndefault == 0);
+ ASSERT_TRUE (val_t::ncopy == 0);
+ ASSERT_TRUE (val_t::nassign == 0);
+ ASSERT_TRUE (val_t::ndtor == 0);
+
+ {
+ Set s;
+ ASSERT_EQ (false, s.add (val_t ()));
+ ASSERT_EQ (true, 1 == s.elements ());
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+ {
+ Set s;
+ ASSERT_EQ (false, s.add (val_t ()));
+ ASSERT_EQ (true, s.add (val_t ()));
+ ASSERT_EQ (true, 1 == s.elements ());
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+
+ {
+ Set s;
+ val_t v1 (1), v2 (2), v3 (3);
+ int ndefault = val_t::ndefault;
+ int nassign = val_t::nassign;
+
+ ASSERT_EQ (false, s.add (v1));
+ ASSERT_EQ (true, s.contains (v1));
+ ASSERT_EQ (true, 1 == s.elements ());
+
+ ASSERT_EQ (false, s.add (v2));
+ ASSERT_EQ (true, s.contains (v2));
+ ASSERT_EQ (true, 2 == s.elements ());
+
+ ASSERT_EQ (false, s.add (v3));
+ ASSERT_EQ (true, s.contains (v3));
+ ASSERT_EQ (true, 3 == s.elements ());
+
+ ASSERT_EQ (true, s.add (v2));
+ ASSERT_EQ (true, s.contains (v2));
+ ASSERT_EQ (true, 3 == s.elements ());
+
+ s.remove (v2);
+ ASSERT_EQ (true, 2 == s.elements ());
+ s.remove (v3);
+ ASSERT_EQ (true, 1 == s.elements ());
+
+ /* Verify that no default ctors or assignment operators have
+ been called. */
+ ASSERT_EQ (true, ndefault == val_t::ndefault);
+ ASSERT_EQ (true, nassign == val_t::nassign);
+ }
+
+ ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
+}
+
/* Run all of the selftests within this file. */
void
hash_set_tests_c_tests ()
{
test_set_of_strings ();
+ test_set_of_type_with_ctor_and_dtor ();
}
} // namespace selftest
#ifndef hash_set_h
#define hash_set_h
+/* Class hash_set is a hash-value based container for objects of
+ KeyId type.
+ KeyId may be a non-trivial (non-POD) type provided a suitabe Traits
+ class. Default Traits specializations are provided for basic types
+ such as integers, pointers, and std::pair. Inserted elements are
+ value-initialized either to zero for POD types or by invoking their
+ default ctor. Removed elements are destroyed by invoking their dtor.
+ On hash_set destruction all elements are removed. Objects of
+ hash_set type are copy-constructible but not assignable. */
+
template<typename KeyId, bool Lazy = false,
typename Traits = default_hash_traits<KeyId> >
class hash_set
Key *e = m_table.find_slot_with_hash (k, Traits::hash (k), INSERT);
bool existed = !Traits::is_empty (*e);
if (!existed)
- *e = k;
+ new (e) Key (k);
return existed;
}
several things.
- A typedef named 'value_type' to the value type (from above).
+ Provided a suitable Descriptor class it may be a user-defined,
+ non-POD type.
- A static member function named 'hash' that takes a value_type
(or 'const value_type &') and returns a hashval_t value.
- A typedef named 'compare_type' that is used to test when a value
- is found. This type is the comparison type. Usually, it will be the
- same as value_type. If it is not the same type, you must generally
- explicitly compute hash values and pass them to the hash table.
+ is found. This type is the comparison type. Usually, it will be
+ the same as value_type and may be a user-defined, non-POD type.
+ If it is not the same type, you must generally explicitly compute
+ hash values and pass them to the hash table.
- A static member function named 'equal' that takes a value_type
and a compare_type, and returns a bool. Both arguments can be
}
private:
+ /* FIXME: Make the class assignable. See pr90959. */
+ void operator= (hash_table&);
+
template<typename T> friend void gt_ggc_mx (hash_table<T> *);
template<typename T> friend void gt_pch_nx (hash_table<T> *);
template<typename T> friend void
if (is_deleted (entry))
mark_deleted (nentries[i]);
else if (!is_empty (entry))
- nentries[i] = entry;
+ new ((void*) (nentries + i)) value_type (entry);
}
m_entries = nentries;
}