From af5f364a5a2bc676e3d6c8ecae9edea580d4ba27 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Thu, 15 Jun 2017 08:04:54 +0200 Subject: [PATCH 1/2] Fix Issue 16659 - Clarify mutating while iterating rules --- spec/hash-map.dd | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 2facff429f..238e3b93e8 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -305,6 +305,32 @@ $(H3 $(LNAME2 construction_and_ref_semantic, Construction and Reference Semantic assert(aa[2] == 2); // now both refer to the same instance ------ +$(H3 $(LNAME2 mutation_while_iteration, Mutation during Iteration)) + + $(P It is legal to change the value of key and remove it from the associative array during an + iteration. However, it it not guaranteed that a newly inserted key will be part of the current iteration. + ) + + ------ + unittest + { + int[string] hash = ["a":5, "b":2, "c": 7, "d": 9, "e": 1]; + foreach (key, ref value; hash) + { + if (value < 5) + hash.remove(key); + else + { + value++; + hash["_"] = 0; // won't be seen during this iteration + hash["foo"] = 0; + } + count++; + } + assert(count == 6); + assert(hash == ["c":8, "a":6, "d":10, "foo": 0, "_": 0]); + } + ------ $(H3 $(LNAME2 properties, Properties)) From 66f0577cd77e0e566dd95b653a16929b1ee15a04 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 16 Jun 2017 00:43:04 +0200 Subject: [PATCH 2/2] Incorporate feedback and reword paragraph --- spec/hash-map.dd | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 238e3b93e8..3860760cc7 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -307,24 +307,38 @@ $(H3 $(LNAME2 construction_and_ref_semantic, Construction and Reference Semantic $(H3 $(LNAME2 mutation_while_iteration, Mutation during Iteration)) - $(P It is legal to change the value of key and remove it from the associative array during an - iteration. However, it it not guaranteed that a newly inserted key will be part of the current iteration. + $(P There's no guarantee that $(I removing) an entry doesn't + trigger a rehashing and that as a consequence the index variable + isn't invalidated and still points to the same, remaining elements + of the ongoing iteration. Similarly $(I inserting) a new key isn't + guaranteed to maintain the same iteration. $(I Modifying) a key needs to + be considered as a removal and insertion and thus can trigger a rehashing + as well. + However, it is legal to $(I change) the associated value during an iteration. + $(I Key lookup) (`key in arr`) is also guaranteed not to cause a rehashing. ) ------ unittest { + import std.stdio : writeln; + int count = 0; + int[string] hash = ["a":5, "b":2, "c": 7, "d": 9, "e": 1]; foreach (key, ref value; hash) { if (value < 5) - hash.remove(key); + hash.remove(key); // DANGEROUS: removal might cause a rehashing else { + // DANGEROUS: insertion might cause a rehashing + // For example, with the current implementation + // the following can be observed: + hash["_"] = 0; // _not_ seen during this iteration + hash["foo"] = 0; // seen during this iteration value++; - hash["_"] = 0; // won't be seen during this iteration - hash["foo"] = 0; } + hash.writeln; count++; } assert(count == 6);