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 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; }