Skip to content

Commit 0a50394

Browse files
Use ordering equivalence for map subscripting (#544)
C++ map membership follows ordering equivalence, not `operator==`. When those comparisons group keys differently, translated subscripting can return another value or replace an existing entry. Both generated map implementations now use the ordering relation for the hit test; explicit equality and lazy default construction are unchanged. `tests/unit/map_key_identity.cpp` exercises both mismatches and matching keys. It checks values, size and traversal, construction count, and saved iterator and value-pointer identity. It also checks that explicit key equality keeps its C++ result. --- Generated by [Perfloop](https://app.perfloop.ai). --------- Signed-off-by: Perfloop Agent <agent@perfloop.ai> Co-authored-by: Perfloop Agent <agent@perfloop.ai>
1 parent 20338d9 commit 0a50394

4 files changed

Lines changed: 1010 additions & 2 deletions

File tree

‎rules/map/shim.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<K: Ord, T> Map<K, T> {
161161

162162
pub fn get_or_insert_with(&mut self, key: K, value: impl FnOnce() -> T) -> Value<T> {
163163
let prev = match self.nodes.range(..=&key).next_back() {
164-
Some((k, node)) if *k == key => {
164+
Some((k, node)) if k.cmp(&key) == std::cmp::Ordering::Equal => {
165165
return node
166166
.borrow()
167167
.value
@@ -376,7 +376,7 @@ impl<K: Ord, T> UnsafeMap<K, T> {
376376
let prev = match self.nodes.range(..=&key).next_back() {
377377
Some((k, node)) => {
378378
let node = &**node as *const UnsafeMapNode<T> as *mut UnsafeMapNode<T>;
379-
if *k == key {
379+
if k.cmp(&key) == std::cmp::Ordering::Equal {
380380
return UnsafeMapNode::value(node);
381381
}
382382
node

‎tests/unit/map_key_identity.cpp‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include <map>
2+
3+
int default_constructions = 0;
4+
5+
struct Key {
6+
int order;
7+
int identity;
8+
};
9+
10+
bool operator<(const Key &a, const Key &b) { return a.order < b.order; }
11+
bool operator==(const Key &a, const Key &b) { return a.identity == b.identity; }
12+
13+
struct Mapped {
14+
int value;
15+
Mapped() : value(0) { ++default_constructions; }
16+
};
17+
18+
int main() {
19+
bool ok = true;
20+
21+
const Key distinct_a{1, 7};
22+
const Key distinct_b{2, 7};
23+
ok &= (distinct_a == distinct_b);
24+
25+
std::map<Key, Mapped> distinct;
26+
Mapped &a = distinct[distinct_a];
27+
a.value = 11;
28+
auto a_it = distinct.find(distinct_a);
29+
Mapped *a_value = &a_it->second;
30+
ok &= (default_constructions == 1);
31+
32+
Mapped &b = distinct[distinct_b];
33+
b.value = 22;
34+
auto b_it = distinct.find(distinct_b);
35+
ok &= (default_constructions == 2);
36+
ok &= (distinct.size() == 2);
37+
ok &= (&a != &b);
38+
ok &= (a.value == 11 && b.value == 22);
39+
ok &= (a_it == distinct.find(distinct_a));
40+
ok &= (b_it == distinct.find(distinct_b));
41+
ok &= (b_it != distinct.end());
42+
ok &= (&a_it->second == a_value);
43+
ok &= (a_it->second.value == 11);
44+
ok &= (distinct_a == distinct_b);
45+
46+
std::size_t distinct_count = 0;
47+
for (const auto &entry : distinct) {
48+
if (distinct_count == 0)
49+
ok &= (entry.first.order == 1 && entry.second.value == 11);
50+
else if (distinct_count == 1)
51+
ok &= (entry.first.order == 2 && entry.second.value == 22);
52+
++distinct_count;
53+
}
54+
ok &= (distinct_count == 2);
55+
56+
const Key equivalent_a{3, 8};
57+
const Key equivalent_b{3, 9};
58+
ok &= !(equivalent_a == equivalent_b);
59+
60+
std::map<Key, Mapped> equivalent;
61+
const int before_equivalent = default_constructions;
62+
Mapped &first = equivalent[equivalent_a];
63+
first.value = 31;
64+
auto first_it = equivalent.find(equivalent_a);
65+
Mapped *first_value = &first_it->second;
66+
ok &= (default_constructions == before_equivalent + 1);
67+
68+
Mapped &second = equivalent[equivalent_b];
69+
const bool reused = default_constructions == before_equivalent + 1;
70+
ok &= reused;
71+
ok &= (equivalent.size() == 1);
72+
if (reused) {
73+
ok &= (first_it != equivalent.end());
74+
ok &= (&first == &second);
75+
ok &= (first_it == equivalent.find(equivalent_a));
76+
ok &= (first_it == equivalent.find(equivalent_b));
77+
ok &= (&first_it->second == first_value);
78+
ok &= (first_it->second.value == 31 && second.value == 31);
79+
80+
std::size_t equivalent_count = 0;
81+
for (const auto &entry : equivalent) {
82+
ok &= (entry.first.order == 3 && entry.second.value == 31);
83+
++equivalent_count;
84+
}
85+
ok &= (equivalent_count == 1);
86+
}
87+
ok &= !(equivalent_a == equivalent_b);
88+
89+
const Key later_key{4, 10};
90+
Mapped &later = equivalent[later_key];
91+
later.value = 41;
92+
ok &= (default_constructions == before_equivalent + 2);
93+
ok &= (equivalent.size() == 2);
94+
if (reused) {
95+
ok &= (first_it == equivalent.find(equivalent_a));
96+
ok &= (&first_it->second == first_value);
97+
ok &= (first_it->second.value == 31);
98+
}
99+
100+
const Key matching_a{5, 5};
101+
const Key matching_b{5, 5};
102+
const Key matching_c{6, 6};
103+
const int before_matching = default_constructions;
104+
std::map<Key, Mapped> matching;
105+
Mapped &matched = matching[matching_a];
106+
matched.value = 51;
107+
auto matched_it = matching.find(matching_a);
108+
Mapped *matched_value = &matched_it->second;
109+
ok &= (default_constructions == before_matching + 1);
110+
111+
Mapped &matched_again = matching[matching_b];
112+
ok &= (default_constructions == before_matching + 1);
113+
ok &= (matching.size() == 1);
114+
ok &= (&matched == &matched_again);
115+
ok &= (matched_it == matching.find(matching_b));
116+
ok &= (&matched_it->second == matched_value);
117+
ok &= (matched_it->second.value == 51);
118+
119+
Mapped &other = matching[matching_c];
120+
other.value = 61;
121+
ok &= (default_constructions == before_matching + 2);
122+
ok &= (matching.size() == 2);
123+
ok &= (matched_it == matching.find(matching_a));
124+
ok &= (&matched_it->second == matched_value);
125+
ok &= (matched_it->second.value == 51);
126+
127+
return ok ? 0 : 1;
128+
}

0 commit comments

Comments
 (0)