[CLJ-944] compiler makes different concrete maps then the reader Created: 04/Mar/12 Updated: 24/May/13 |
|
| Status: | Open |
| Project: | Clojure |
| Component/s: | None |
| Affects Version/s: | Release 1.3, Release 1.4, Release 1.5 |
| Fix Version/s: | Release 1.5, Release 1.6 |
| Type: | Defect | Priority: | Major |
| Reporter: | Alf Kristian Støyle | Assignee: | Unassigned |
| Resolution: | Unresolved | Votes: | 2 |
| Labels: | None | ||
| Environment: |
Clojure 1.3 om Mac OS 10.7, Clojure 1.5.0 alpha1 on Linux x86_64 (OpenJDK 1.7.0 b147) |
||
| Attachments: |
|
| Patch: | Code and Test |
| Approval: | Screened |
| Description |
|
I (Stu) agree with Nicola's assessment in the comments: the single real problem here is that the compiler's MapExpr parser emits maps differently from other map-makers, e.g. RT's factory functions. Patch clj944-plus-tests does three things:
Original report follows: Hi guys, I am getting the following exception: (.containsKey {:one 1} :one)
;=> ClassCastException clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.PersistentHashMap
The map is a clojure.lang.PersistentArrayMap, which obviously has a containsKey method (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentArrayMap.java#L95). Casting it works fine though: (.containsKey ^clojure.lang.PersistentArrayMap {:one 1} :one)
;=> true
The mailing list suggest that the compiler injects an incorrect cast to clojure.lang.PersistentHashMap. In this case it should probably be cast to a clojure.lang.Associative, the highest common interface having the .containsKey method. The problem is not present in Clojure 1.2.1. |
| Comments |
| Comment by Nicola Mometto [ 30/Oct/12 5:02 PM ] |
|
The attached patch fixes the issue, by emitting IPersistentMap instead of Persistent{Hash|Array}Map as class type for maps literals |
| Comment by Nicola Mometto [ 01/Nov/12 3:48 PM ] |
|
I uploaded another patch fixing the same problem in a different way. This approach is more consistent, making the type of the compiler's internal representation of a map literal equal to the one of the reader. Note that this second approach while being more consistent, breaks some tests that assume some operations on maps (specifically `seq` and `print`) to be order dependent, and written with the hash-map return order implementation in mind. That should not be the case and if the second patch is preferred over the first one, I'll gladly fix those tests. |
| Comment by Stuart Halloway [ 01/Mar/13 12:09 PM ] |
|
Approach #2, relying on consistent choice of concrete map class by size throughout, feels quite fragile. Approach #1 seems to abuse the method name getJavaClass(), now having it return "get the base type I would need for cast". Maybe there needs to be a different thing entirely? |
| Comment by Nicola Mometto [ 01/Mar/13 2:17 PM ] |
|
Patch #2 should get merged (IMHO) regardless of the fragility of its approach to fixing this ticket's bug, since it fixes another bug: prior to the patch: user=> (class {:a 1}) after the patch: user=> (class {:a 1}) This should also lead to some minor performance enhancement since prior to this moment, every map def'ed would be a HashMap instead of an ArrayMap So, I think patch #2 should be applied if not for this ticket's bug, at least for the reason stated above. |
| Comment by Rich Hickey [ 13/Apr/13 9:41 AM ] |
|
This should not have passed screening. There are two issues, should be separate. I have no idea what has been screened nor what will be applied should it be approved. There's contention in the discussion but no resolution. |
| Comment by Nicola Mometto [ 13/Apr/13 12:06 PM ] |
|
I don't think that there are two issues here. The underlying cause of those two symtoms is fixed by the patch 002 that i submitted (incorporated in Stu's clj944-plus-tests patch. Stuart said that this approach feels fragile but the bug is caused by the fact that everywhere else clojure returns a PersistentArrayMap when the element count is <= than the PersistentHashMap threshold, and when emitting maps, it doesn't. Making clojure emit maps consistently with how clojure does internally everywhere else looks to me like the only solution, and I don't really see how making clojure consistent is a fragile approach. But again, if somebody can suggest a better solution to this problem, I'll gladly submit another patch. |