Hi all,
The copy-hash function has been broken since May 2016.
The hash-diff function is also broken, since it depends
on it.
The patch follows:
From: Kaz Kylheku <kaz@???>
Date: Mon, 23 Oct 2017 06:35:35 -0700
Subject: [PATCH] hash: fix broken copy_hash.
Impact assessment: this bug affects the correctness of
all programs which rely on copying hash tables. Direct
reliance means the use of copy-hash, or using the generic copy
function on hash objects. Indirect reliance occurs through
hash-diff which uses copy-hash. Nothing in TXR itself calls
hash-diff. The the listener's Tab completion relies on
copy-hash for package-sensitive symbol visibility calculation.
Since that is an interactive feature, the impact is low.
* hash.c (copy_hash_chain): New static function.
(copy_hash): Use copy_hash_chain instead of copy_alist,
since the pairs are hash conses and not regular conses:
they have a hash value field that must be copied.
---
hash.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/hash.c b/hash.c
index 8d3f2b3..444a32b 100644
--- a/hash.c
+++ b/hash.c
@@ -677,6 +677,20 @@ val make_similar_hash(val existing)
return hash;
}
+static val copy_hash_chain(val chain)
+{
+ list_collect_decl(out, ptail);
+
+ for (; chain; chain = cdr(chain)) {
+ val entry = car(chain);
+ val nentry = cons(car(entry), cdr(entry));
+ nentry->ch.hash = entry->ch.hash;
+ ptail = list_collect(ptail, nentry);
+ }
+
+ return out;
+}
+
val copy_hash(val existing)
{
struct hash *ex = coerce(struct hash *, cobj_handle(existing,
hash_s));
@@ -698,7 +712,7 @@ val copy_hash(val existing)
h->acons_new_c_fun = ex->acons_new_c_fun;
for (iter = zero; lt(iter, mod); iter = plus(iter, one))
- set(vecref_l(h->table, iter), copy_alist(vecref(ex->table, iter)));
+ set(vecref_l(h->table, iter), copy_hash_chain(vecref(ex->table,
iter)));
return hash;
}