Share the const-keyword-lookup head predicates
The inliner and the type inferencer each recognized (:k m) and (get m :k) lookups with their own copy of the callee tests — the get-callee check was duplicated verbatim across both. Lift kw-callee?/get-callee? into jolt.passes.fold (alongside scalar-const?) and call them from both passes so the head recognition can't drift. Only the head predicates move. The deliberate differences stay: the inliner still accepts any scalar key in the get-form (its scalar-replacement targets can be string/number-keyed maps) while the inferencer requires keyword keys for struct field typing, and the inferencer keeps its two arms separate so each rebuilds args for its form. The backend's value-as-fn ifn-kind is left alone.
This commit is contained in:
parent
e9d56422bd
commit
a893e21111
5 changed files with 91 additions and 73 deletions
|
|
@ -73,3 +73,19 @@
|
|||
(defn scalar-const? [n]
|
||||
(and (= :const (get n :op))
|
||||
(let [v (get n :val)] (or (keyword? v) (string? v) (number? v) (boolean? v)))))
|
||||
|
||||
;; The two callee shapes a constant-keyword map lookup takes: a keyword in fn
|
||||
;; position — (:k m) — or clojure.core/get with a const key — (get m :k). The
|
||||
;; inliner (scalar replacement) and the type inferencer both recognize these;
|
||||
;; sharing the head predicates keeps the two from drifting. Each caller still
|
||||
;; imposes its own arity, subject, and key-type constraints.
|
||||
(defn kw-callee?
|
||||
"True if fnode is a constant keyword used as a function head — the (:k m) form."
|
||||
[fnode]
|
||||
(and (= :const (get fnode :op)) (keyword? (get fnode :val))))
|
||||
|
||||
(defn get-callee?
|
||||
"True if fnode is the clojure.core/get (or host get) callee — the (get m k) form."
|
||||
[fnode]
|
||||
(or (and (= :var (get fnode :op)) (= "clojure.core" (get fnode :ns)) (= "get" (get fnode :name)))
|
||||
(and (= :host (get fnode :op)) (= "get" (get fnode :name)))))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
and the `dirty` fixpoint flag. Portable Clojure (compiler-tier)."
|
||||
(:require [jolt.host :refer [inline-ir]]
|
||||
[jolt.ir :refer [map-ir-children reduce-ir-children coerce-node]]
|
||||
[jolt.passes.fold :refer [scalar-const?]]))
|
||||
[jolt.passes.fold :refer [scalar-const? kw-callee? get-callee?]]))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Shared state: a dirty flag the fixpoint loop reads, and a fresh-name counter
|
||||
|
|
@ -229,7 +229,7 @@
|
|||
(defn- pure-fn? [f]
|
||||
(let [op (get f :op)]
|
||||
(cond
|
||||
(and (= op :const) (keyword? (get f :val))) true
|
||||
(kw-callee? f) true
|
||||
(= op :var) (and (= "clojure.core" (get f :ns)) (contains? pure-fns (get f :name)))
|
||||
(= op :host) (contains? pure-fns (get f :name))
|
||||
:else false)))
|
||||
|
|
@ -282,13 +282,12 @@
|
|||
(if (= :invoke (get node :op))
|
||||
(let [f (get node :fn) args (get node :args)]
|
||||
(cond
|
||||
(and (= :const (get f :op)) (keyword? (get f :val))
|
||||
(and (kw-callee? f)
|
||||
(= 1 (count args))
|
||||
(= :local (get (nth args 0) :op)) (= nm (get (nth args 0) :name)))
|
||||
(get f :val)
|
||||
|
||||
(and (or (and (= :var (get f :op)) (= "clojure.core" (get f :ns)) (= "get" (get f :name)))
|
||||
(and (= :host (get f :op)) (= "get" (get f :name))))
|
||||
(and (get-callee? f)
|
||||
(= 2 (count args))
|
||||
(= :local (get (nth args 0) :op)) (= nm (get (nth args 0) :name))
|
||||
(scalar-const? (nth args 1)))
|
||||
|
|
@ -500,7 +499,7 @@
|
|||
lookup folds only for a declared field."
|
||||
[node]
|
||||
(let [f (get node :fn) args (get node :args)]
|
||||
(if (and (= :const (get f :op)) (keyword? (get f :val)) (= 1 (count args)))
|
||||
(if (and (kw-callee? f) (= 1 (count args)))
|
||||
(let [m (nth args 0) k (get f :val)]
|
||||
(if (and (= :map (get m :op)) (const-key-map? m) (all-vals-pure? m))
|
||||
(do (mark!) (map-val m k))
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
top = :any) that types expressions and reuses the same walk as a loose success
|
||||
checker. Also the inter-procedural driver API the back end calls to
|
||||
propagate param types across a unit / the whole program. Weakly coupled to the
|
||||
IR-rewriting passes — shares only the const-shape predicate (jolt.passes.fold)."
|
||||
(:require [jolt.passes.fold :refer [scalar-const?]]
|
||||
IR-rewriting passes — shares the const-shape predicates (jolt.passes.fold)."
|
||||
(:require [jolt.passes.fold :refer [scalar-const? kw-callee? get-callee?]]
|
||||
[jolt.passes.types.lattice :refer
|
||||
[velem selem sfields vec-type? set-type? struct-type? mk-vec mk-set
|
||||
mk-struct union-cap scalar-t? union-type? umembers union-of merge-fields
|
||||
|
|
@ -263,7 +263,7 @@
|
|||
;; (:k m) / (:k m default): the result is m's field type, and if m is a
|
||||
;; struct the subject is tagged so the back end drops the guard — this
|
||||
;; types nested access end to end (RFC 0005).
|
||||
(and (= :const (get fnode :op)) (keyword? (get fnode :val)) (>= n 1) (<= n 2))
|
||||
(and (kw-callee? fnode) (>= n 1) (<= n 2))
|
||||
(let [mr (infer (nth args 0) tenv env)
|
||||
mt (nth mr 0)
|
||||
msub (if (struct-safe? mt) (mark-struct (nth mr 1) mt) (nth mr 1))
|
||||
|
|
@ -272,8 +272,7 @@
|
|||
[(if dr (join ft (nth dr 0)) ft)
|
||||
(assoc node :args (if dr [msub (nth dr 1)] [msub]))])
|
||||
;; (get m :k [default]): same, when the key is a constant keyword.
|
||||
(and (or (and (= :var (get fnode :op)) (= "clojure.core" (get fnode :ns)) (= "get" (get fnode :name)))
|
||||
(and (= :host (get fnode :op)) (= "get" (get fnode :name))))
|
||||
(and (get-callee? fnode)
|
||||
(>= n 2) (= :const (get (nth args 1) :op)) (keyword? (get (nth args 1) :val)))
|
||||
(let [mr (infer (nth args 0) tenv env)
|
||||
mt (nth mr 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue