git » blacktide.git » master » tree

[master] / src / com / tailrecursion / blacktide / compiler / passes / collections.clj

(ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.collections
  "Preserve native constant collections and construction checks. Metadata keeps
  construction dynamic and is evaluated only after that construction succeeds."
  (:require [com.tailrecursion.nanopass :as np]
            [com.tailrecursion.nanopass.pipeline :as pipeline]
            [com.tailrecursion.blacktide.compiler.languages :as l]
            [com.tailrecursion.blacktide.compiler.passes.host-emit :as host]
            [typed.clojure :as-alias t]))

(np/defpass constant
  {:from [l/Collections CollectionKind] :to t/Any :args [(t/Vec t/Any)] :body true}
  [values kind]
  (case kind
    :vector (vec values)
    :map (reduce (fn [^{::t/- (t/Map t/Any t/Any)} out ^{::t/- (t/Seq t/Any)} pair]
                   (assoc out (first pair) (second pair))) {} (partition 2 values))
    :set (set values)))

(np/defpass form
  {:from [l/Collections CollectionKind] :to [l/Machine Code]
   :args [(t/Vec l/Machine-Code) t/Bool] :body true}
  [items unique? kind]
  (if (= :vector kind)
    (if (<= (count items) 6)
      (list* 'clojure.lang.Tuple/create items)
      ;; An Object expression prevents constant folding without unboxing the
      ;; first item or changing its numeric width.
      (list 'do (assoc items 0 (host/box-form (nth items 0)))))
    (list (if (= :set kind) 'clojure.lang.RT/set
              (if unique? 'clojure.lang.RT/mapUniqueKeys 'clojure.lang.RT/map))
          (list 'clojure.lang.RT/object_array items))))

(np/defpass literal-values
  {:from [l/Collections Exprs] :to (t/Option (t/Vec t/Any)) :body true}
  [items]
  (reduce (fn [^{::t/- (t/Option (t/Vec t/Any))} values ^{::t/- l/Collections-Expr} expr]
            (let [node (:node expr)]
              (if (and values (= :literal (:op node))) (conj values (:value node)) (reduced nil))))
          [] items))

(np/defpass metadata-form
  {:from [l/Machine Code] :to [l/Machine Code] :args [l/Machine-Code] :body true}
  [value metadata]
  (let [v (gensym "value__") m (gensym "metadata__")]
    (list 'let* [(with-meta v {:tag 'clojure.lang.IObj}) value
                 (with-meta m {:tag 'clojure.lang.IPersistentMap}) metadata]
          (list '. v 'withMeta m))))

(np/defpasses
  (normalize-forms
    {:from [l/Assigned Exprs] :to [l/Collections Exprs] :processors {Expr normalize}}
    [forms])

  (normalize-node
    {:from [l/Assigned Node] :to [l/Collections Node]
     :processors {Expr normalize Exprs normalize-forms Binding normalize-binding Raw clojure.core/identity
                  Local clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity}}
    [node]
    :collection
    (let [items (mapv normalize (:items node)) metadata (some-> (:metadata node) normalize)
          values (literal-values items)
          keys (when (= :map (:kind node)) (literal-values (vec (take-nth 2 items))))
          unique? (boolean (and keys (= (count keys) (count (set keys)))))]
      (when (and (nil? metadata) keys (not unique?))
        (throw (IllegalArgumentException. "Duplicate constant keys in map")))
      (if (and (nil? metadata) values)
        {:op :literal :value (constant values (:kind node))}
        {:op :build :kind (:kind node) :items items :metadata metadata :unique? unique?})))

  (normalize-binding
    {:from [l/Assigned Binding] :to [l/Collections Binding]
     :processors {Expr normalize Local clojure.core/identity}}
    [binding])

  (normalize
    {:from [l/Assigned Expr] :to [l/Collections Expr] :body true}
    [expr]
    (assoc expr :node (normalize-node (:node expr)))))

(np/defpass stage
  {:from [l/Assigned Compilation] :to [l/Collections Compilation] :body true}
  [compilation]
  (pipeline/map-value normalize compilation))