Clojure

Persistent assoc/conj on a transient-created collision node

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Completed
  • Affects Version/s: Release 1.5
  • Fix Version/s: Release 1.6
  • Component/s: None
  • Labels:
  • Patch:
    Code and Test
  • Approval:
    Ok

Description

Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients were introduced the invariant array.length == count*2 doesn't hold for HashCollisionNode.
However persistent .without still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))

returns false.

Patch: persistent-assoc-after-collision.diff

Generative test patch: transient-generative-test.diff

The generative test reliably reproduces the error. It is simpler than the original test that found the bug but tests a series conj/disj/transient/persistent actions on a set. I've included it separately in case we decide not to apply.

Screened by: Alex Miller

Activity

Christophe Grand made changes -
Field Original Value New Value
Description Since transients for HashCollisionNode the invariant array.length == count*2 doesn't hold.
However persistent without still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false
Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients for HashCollisionNode the invariant array.length == count*2 doesn't hold.
However persistent without still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false
Affects Version/s Release 1.4 [ 10040 ]
Affects Version/s Release 1.3 [ 10038 ]
Alex Miller made changes -
Approval Triaged [ 10120 ]
Labels transient
Alex Miller made changes -
Priority Major [ 3 ] Critical [ 2 ]
Rich Hickey made changes -
Approval Triaged [ 10120 ] Vetted [ 10003 ]
Fix Version/s Release 1.6 [ 10157 ]
Christophe Grand made changes -
Description Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients for HashCollisionNode the invariant array.length == count*2 doesn't hold.
However persistent without still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false
Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients were introduced the invariant array.length == count*2 doesn't hold for HashCollisionNode.
However persistent {{.without}} still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false
Alex Miller made changes -
Approval Vetted [ 10003 ] Screened [ 10004 ]
Description Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients were introduced the invariant array.length == count*2 doesn't hold for HashCollisionNode.
However persistent {{.without}} still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false
Bug reported by Zach Tellman https://groups.google.com/d/msg/clojure-dev/HvppNjEH5Qc/1wZ-6qE7nWgJ

Since transients were introduced the invariant array.length == count*2 doesn't hold for HashCollisionNode.
However persistent {{.without}} still relies on it.

Hence persistent dissoc on a collision node created by transients fails.

{code}
(let [a (reify Object (hashCode [_] 42))
      b (reify Object (hashCode [_] 42))]
      (= (-> #{a b} transient (disj! a) persistent! (conj a))
       (-> #{a b} transient (disj! a) persistent! (conj a))))
{code}

returns false.

*Patch:* persistent-assoc-after-collision.diff

*Generative test patch:* transient-generative-test.diff

The generative test reliably reproduces the error. It is simpler than the original test that found the bug but tests a series conj/disj/transient/persistent actions on a set. I've included it separately in case we decide not to apply.

*Screened by:* Alex Miller
Attachment transient-generative-test.diff [ 12426 ]
Rich Hickey made changes -
Approval Screened [ 10004 ] Ok [ 10007 ]
Alex Miller made changes -
Resolution Completed [ 1 ]
Status Open [ 1 ] Closed [ 6 ]

People

Vote (4)
Watch (3)

Dates

  • Created:
    Updated:
    Resolved: