test/core: cover ns form + :use; fix use-impl spec parsing

Checking test coverage for the namespace tiers surfaced a real bug nothing
exercised: use-impl checked (array? s) to find a spec's ns symbol, but a
vector spec like [src.x] is a pvec/tuple, not a Janet array — so (:use ...)
and standalone (use ...) errored. Fixed to coerce (pvec->array) then take the
head when the spec is indexed (matching require-impl).

Added coverage that was missing:
- conformance (x3 modes): the (ns …) form itself + :use refers.
- namespaces-spec: (ns …) form, :use, standalone use.

The :use/(use …) path had ZERO tests before, which is why the bug slipped
through earlier tiers.

EBNF needs no change — it's a reader/syntax grammar with no special-forms
enumeration; ns/require/protocol syntax is unchanged (the destructuring note
was already updated in jolt-f79).

Gate green: conformance 269x3, fallback-zero 38/4, self-host, sci-bootstrap,
bootstrap-fixpoint, staged-bootstrap, clojure-test-suite >=4034/67, namespace,
all unit + spec (namespaces 27/27).
This commit is contained in:
Yogthos 2026-06-09 16:23:54 -04:00
parent 8e98a16e25
commit 0626b66987
3 changed files with 11 additions and 2 deletions

View file

@ -774,11 +774,13 @@
(defn use-impl (defn use-impl
"(use '[ns ...] ...) — refer ALL public vars of each used ns into the CURRENT ns. "(use '[ns ...] ...) — refer ALL public vars of each used ns into the CURRENT ns.
A fn; quoted specs arrive evaluated. Each spec is a ns symbol or [ns & opts]." A fn; quoted specs arrive evaluated. Each spec is a ns symbol or a [ns & opts]
vector (a pvec/tuple, not a Janet array — coerce, then take the head as the ns)."
[ctx & specs] [ctx & specs]
(def target-ns (ctx-find-ns ctx (ctx-current-ns ctx))) (def target-ns (ctx-find-ns ctx (ctx-current-ns ctx)))
(each s specs (each s specs
(let [ns-sym (if (array? s) (in s 0) s) (let [spec (if (pvec? s) (pv->array s) s)
ns-sym (if (indexed? spec) (in spec 0) spec)
src-name (sym-name-str ns-sym)] src-name (sym-name-str ns-sym)]
(maybe-require-ns ctx src-name) (maybe-require-ns ctx src-name)
(let [source-ns (ctx-find-ns ctx src-name)] (let [source-ns (ctx-find-ns ctx src-name)]

View file

@ -135,6 +135,8 @@
### ---- HIGH: aliased namespace calls ---- ### ---- HIGH: aliased namespace calls ----
["require :as alias" "\"1,2,3\"" "(do (require (quote [clojure.string :as s])) (s/join \",\" [1 2 3]))"] ["require :as alias" "\"1,2,3\"" "(do (require (quote [clojure.string :as s])) (s/join \",\" [1 2 3]))"]
["ns form + alias" "\"HI\"" "(do (ns my.a (:require [clojure.string :as s])) (s/upper-case \"hi\"))"]
["ns :use refers" "42" "(do (ns src.u) (def helper 42) (ns dst.u (:use [src.u])) helper)"]
### ---- MED: missing core fns ---- ### ---- MED: missing core fns ----
["peek vec" "3" "(peek [1 2 3])"] ["peek vec" "3" "(peek [1 2 3])"]

View file

@ -16,6 +16,11 @@
(defspec "namespaces / ns operations" (defspec "namespaces / ns operations"
["in-ns switches" "true" "(do (in-ns 'my.ns) (symbol? 'x))"] ["in-ns switches" "true" "(do (in-ns 'my.ns) (symbol? 'x))"]
# ns is a macro over in-ns/require/use/import (Stage 2 jolt-eaa): the form sets
# the current ns and processes its clauses.
["ns form + alias" "\"HI\"" "(do (ns my.app (:require [clojure.string :as s])) (s/upper-case \"hi\"))"]
["ns :use refers all" "9" "(do (ns src.lib) (def helper 9) (ns dst.app (:use [src.lib])) helper)"]
["standalone use" "7" "(do (ns src.l2) (def k 7) (in-ns 'dst.a2) (use '[src.l2]) k)"]
["ns-name" "true" "(do (require (quote [clojure.string])) (= 'clojure.string (ns-name (find-ns 'clojure.string))))"] ["ns-name" "true" "(do (require (quote [clojure.string])) (= 'clojure.string (ns-name (find-ns 'clojure.string))))"]
["find-ns existing" "true" "(some? (find-ns 'clojure.core))"] ["find-ns existing" "true" "(some? (find-ns 'clojure.core))"]
["find-ns missing" "nil" "(find-ns 'does.not.exist)"] ["find-ns missing" "nil" "(find-ns 'does.not.exist)"]