Skip to content

Namespace resolution prefers .clj over .cljc, diverging from ClojureCLR #116

Description

@skydread1

Problem

MAGIC resolves a namespace to a source file in the order .cljr, .clj, .cljc. Stock ClojureCLR uses .cljr, .cljc, .clj (Clojure/Clojure/Lib/RT.cs:3584). When a library ships both foo.clj (JVM-only) and foo.cljc (portable), cljr loads the .cljc and nos loads the .clj, so MAGIC picks the file that cannot work:

$ nos build          # src/precedence/demo.cljc and src/precedence/demo.clj both present
Compiling precedence.demo
System.InvalidOperationException: Could not find type java.util.ArrayList during import

The order lives in two places on purpose, since build.clj compiles magic.api before clojure.core: magic-compiler/src/magic/api.clj:37 and magic-compiler/src/stdlib/clojure/core.clj:5971.

Three more places assume .clj is the only source extension, so swapping the order alone is not enough:

  • magic.api/compile-file hardcodes .clj in the assembly name, so a .cljc namespace emits foo.clj.dll while -load probes foo.cljc.dll and never finds it.
  • magic-unity's editor hooks match .clj.dll by suffix (PlayerCljAssemblies, StockClojureCoexistence), so a .cljc.dll ships without the IL2CPP AOT workarounds and fails on device rather than in CI.
  • -load resolves the source file and the compiled assembly with two independent searches and then compares their timestamps, so foo.cljc can be compared against a foo.clj.dll built from a different file. Renaming foo.clj to foo.cljc leaves the old assembly beside it, and a rename does not change the file's mtime, so the stale DLL still wins and the rename silently has no effect.

magic-compiler/test/magic/test/load.clj covers .cljr against each of the other two and nothing covers the pair that differs.

Suggestion

(def ^:private source-extensions [".cljr" ".cljc" ".clj"])

in both files, with a load.clj deftest asserting the .cljc wins when both exist. Both are bootstrap namespaces, so the refresh is bb bootstrap, not bb refresh-stdlib.

Alongside that, derive the assembly name from the resolved source's extension rather than hardcoding .clj, give the editor hooks a single predicate covering all three suffixes, and resolve the source first so the assembly candidate can only be the one built from it.

Metadata

Metadata

Assignees

Labels

comp:magic-compilerClojure compiler and stdlibcomp:magic-unityUnity UPM package and IL2CPP workaroundscomp:unity-examplesExample Unity projects under unity-examples/ (smoke + coexist)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions