| author | Alan Dipert
<alan@dipert.org> 2026-11-11 03:23:48 UTC |
| committer | Alan Dipert
<alan@dipert.org> 2026-11-11 03:23:48 UTC |
| parent | 7b795a3d30fb450e4f9638e9f2b5e831ee9ad04c |
| doc/compiler.md | +1 | -1 |
| doc/tutorial.md | +3 | -9 |
| src/com/tailrecursion/blacktide/compiler.clj | +1 | -2 |
| src/com/tailrecursion/blacktide/compiler/languages.clj | +3 | -6 |
| src/com/tailrecursion/blacktide/compiler/passes/assignment.clj | +46 | -83 |
| src/com/tailrecursion/blacktide/compiler/passes/collections.clj | +8 | -0 |
| src/com/tailrecursion/blacktide/compiler/passes/emit.clj | +1 | -4 |
| src/com/tailrecursion/blacktide/compiler/passes/invariants.clj | +1 | -6 |
| src/com/tailrecursion/blacktide/compiler/passes/source.clj | +4 | -10 |
| src/com/tailrecursion/blacktide/compiler/passes/source_host.clj | +5 | -5 |
| src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj | +1 | -4 |
| test/com/tailrecursion/blacktide/check.clj | +5 | -5 |
diff --git a/doc/compiler.md b/doc/compiler.md index 463dde5..6bf5e9d 100644 --- a/doc/compiler.md +++ b/doc/compiler.md @@ -24,7 +24,7 @@ For a walkthrough with actual inputs, intermediate data and generated code, see `compiler/compile-form` orchestrates source analysis and lowering: ```text -Bound → Classified → Sequential → Assigned → Collections +Bound → Classified → Assigned → Collections → Prepared → Ordered → Symbolic → CFG ``` diff --git a/doc/tutorial.md b/doc/tutorial.md index c54a58a..a1cd658 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -118,14 +118,9 @@ The [pipeline](compiler.md) follows the [nanopass approach](https://nanopass.org The representations here are ordinary Clojure maps and vectors. These are _intermediate representations_, or IRs: data describing the program between source and output. Each tree node's `:op` identifies its kind; other fields hold its operands, bindings or body. -_Lowering_ rewrites a richer construct into simpler operations. For example, assignment lowering takes two passes: +_Lowering_ rewrites a richer construct into simpler operations. The assignment pass takes `Classified` to `Assigned`, replacing both assignment forms with individual `:write` nodes. For parallel batches, it introduces bindings to save all right-hand-side values before writing. -| Pass | Input → output language | What the next pass can rely on | -| --- | --- | --- | -| Lower sequential assignment | `Classified` → `Sequential` | `:setq` nodes are gone; their pairs have become individual `:write` nodes. | -| Lower parallel assignment | `Sequential` → `Assigned` | `:psetq` nodes are gone; each batch's right-hand-side values are saved before its pending writes. | - -Later passes can work with individual writes without having to understand the two assignment forms again. The [language definitions](../src/com/tailrecursion/blacktide/compiler/languages.clj) record that progression: `Sequential` removes `:setq` and adds `:write`; `Assigned` then removes `:psetq`. These are contracts the machinery can use. +Later passes can work with individual writes without understanding `setq` and `psetq`. The [language definitions](../src/com/tailrecursion/blacktide/compiler/languages.clj) express that contract: `Assigned` removes both assignment forms and adds `:write`. Blacktide's [nanopass library](nanopass.md) generates code to walk unchanged node kinds and derives [Typed Clojure](https://github.com/typedclojure/typedclojure) types from the language descriptions. Development checks verify pass bodies against those contracts. A pass can still produce a beautifully well-typed wrong answer, so we also need tests of the program's behavior. @@ -160,8 +155,7 @@ The remaining examples use `stages` to run the pipeline and retain intermediate :site {:form form :file nil :line nil :column nil}} [supply bound] (source/parse-scope {:prefix "demo" :next 0} ctx true (vec form)) classified (boundaries/classify bound) - sequential (assignment/sequential classified) - [supply assigned] (assignment/parallel supply sequential) + [supply assigned] (assignment/lower supply classified) constructed (collections/normalize assigned) prepared (prepare/prepare constructed) [supply ordered] (order/normalize supply prepared) diff --git a/src/com/tailrecursion/blacktide/compiler.clj b/src/com/tailrecursion/blacktide/compiler.clj index cb95f37..0e02da3 100644 --- a/src/com/tailrecursion/blacktide/compiler.clj +++ b/src/com/tailrecursion/blacktide/compiler.clj @@ -52,8 +52,7 @@ [compilation] (-> compilation boundaries/stage - assignment/sequential-stage - assignment/parallel-stage + assignment/stage collections/stage prepare/stage order/stage diff --git a/src/com/tailrecursion/blacktide/compiler/languages.clj b/src/com/tailrecursion/blacktide/compiler/languages.clj index 26568c2..a613837 100644 --- a/src/com/tailrecursion/blacktide/compiler/languages.clj +++ b/src/com/tailrecursion/blacktide/compiler/languages.clj @@ -38,7 +38,7 @@ Plan (t/HMap :mandatory {:operation Operation :conversions (t/Vec (t/Option Conversion))} :complete? true) Selection (t/HMap :mandatory {:method (t/Option Method) :exact? t/Bool :tied? t/Bool} :complete? true)}) -;; Source normalization: Bound -> Classified -> Sequential -> Assigned -> +;; Source normalization: Bound -> Classified -> Assigned -> ;; Collections -> Prepared -> Ordered. Native is a restricted subtree. (np/deflanguage Bound {Raw t/Any @@ -97,13 +97,10 @@ {:add {Node [(t/HMap :mandatory {:op ':native :value l/Native-Expr} :complete? true)]}}) -(np/derive-language Sequential Classified - {:remove {Node #{:setq}} +(np/derive-language Assigned Classified + {:remove {Node #{:setq :psetq}} :add {Node [(t/HMap :mandatory {:op ':write :target Local :value Expr} :complete? true)]}}) -(np/derive-language Assigned Sequential - {:remove {Node #{:psetq}}}) - (np/derive-language Collections Assigned {:remove {Node #{:collection}} :add {Node [(t/HMap :mandatory {:op ':build :kind CollectionKind :items Exprs :unique? t/Bool diff --git a/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj b/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj index e53aa06..9f9bdcf 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/assignment.clj @@ -1,78 +1,43 @@ (ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.assignment - "Lower sequential batches to writes, then parallel batches to saved RHSs and - writes. Pending parallel writes do not execute if an RHS abandons evaluation." + "Eliminate assignment batches. Sequential pairs become writes; parallel pairs + save all RHSs before writing. An abandoned RHS leaves pending writes undone." (:require [com.tailrecursion.nanopass :as np] - [com.tailrecursion.nanopass.pipeline :as pipeline] [com.tailrecursion.blacktide.compiler.languages :as l] [com.tailrecursion.blacktide.compiler.passes.source :as source] [typed.clojure :as-alias t])) (np/defpasses - (sequential-pair - {:from [l/Classified Assignment] :to [l/Sequential Assignment] - :processors {Local clojure.core/identity Expr sequential}} - [pair]) - - (sequential-pairs - {:from [l/Classified Assignments] :to [l/Sequential Assignments] :processors {Assignment sequential-pair}} - [pairs]) - - (sequential-forms - {:from [l/Classified Exprs] :to [l/Sequential Exprs] :processors {Expr sequential}} - [forms]) - - (sequential-node - {:from [l/Classified Node] :to [l/Sequential Node] - :processors {Local clojure.core/identity Raw clojure.core/identity CollectionKind clojure.core/identity RecurTarget clojure.core/identity CaseSpec clojure.core/identity - Expr sequential Exprs sequential-forms Binding sequential-binding Assignments sequential-pairs}} - [node] - :setq {:op :do :forms - (mapv (fn [pair] - (let [value (sequential (:value pair))] - (assoc value :node {:op :write :target (:target pair) :value value}))) (:pairs node))}) - - (sequential-binding - {:from [l/Classified Binding] :to [l/Sequential Binding] - :processors {Local clojure.core/identity Expr sequential}} - [binding]) - - (sequential - {:from [l/Classified Expr] :to [l/Sequential Expr] :body true} - [expr] - (assoc expr :node (sequential-node (:node expr))))) - -(np/defpasses - (parallel-forms - {:from [l/Sequential Exprs] :to (t/HVec [l/Bound-Supply l/Assigned-Exprs]) + (lower-forms + {:from [l/Classified Exprs] :to (t/HVec [l/Bound-Supply l/Assigned-Exprs]) :args [l/Bound-Supply] :body true} [supply forms] - (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Exprs])} acc ^{::t/- l/Sequential-Expr} expr] - (let [[supply out] acc [supply expr] (parallel supply expr)] [supply (conj out expr)])) + (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Exprs])} acc ^{::t/- l/Classified-Expr} expr] + (let [[supply out] acc [supply expr] (lower supply expr)] [supply (conj out expr)])) [supply []] forms)) - (parallel-bindings - {:from [l/Sequential Bindings] :to (t/HVec [l/Bound-Supply l/Assigned-Bindings]) + (lower-bindings + {:from [l/Classified Bindings] :to (t/HVec [l/Bound-Supply l/Assigned-Bindings]) :args [l/Bound-Supply] :body true} [supply bindings] - (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings])} acc ^{::t/- l/Sequential-Binding} binding] - (let [[supply out] acc [supply init] (parallel supply (:init binding))] + (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings])} acc ^{::t/- l/Classified-Binding} binding] + (let [[supply out] acc [supply init] (lower supply (:init binding))] [supply (conj out (assoc binding :init init))])) [supply []] bindings)) - (save-parallel-values - {:from [l/Sequential Assignments] + (save-values + {:from [l/Classified Assignments] :to (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs]) :args [l/Bound-Supply] :body true} [supply pairs] (reduce (fn [^{::t/- (t/HVec [l/Bound-Supply l/Assigned-Bindings l/Assigned-Exprs])} acc - ^{::t/- l/Sequential-Assignment} pair] - (let [[supply bindings writes] acc [supply value] (parallel supply (:value pair)) + ^{::t/- l/Classified-Assignment} pair] + (let [[supply bindings writes] acc [supply value] (lower supply (:value pair)) [supply id] (source/fresh "parallel" supply) local {:id id :name id :mutable? false :external? false :jvm (:jvm value)} read (assoc value :node {:op :local :local local}) write (assoc value :node {:op :write :target (:target pair) :value read})] [supply (conj bindings {:local local :init value}) (conj writes write)])) [supply [] []] pairs)) - (parallel-node - {:from [l/Sequential Node] :to (t/HVec [l/Bound-Supply l/Assigned-Node]) :args [l/Bound-Supply]} + (lower-node + {:from [l/Classified Node] :to (t/HVec [l/Bound-Supply l/Assigned-Node]) :args [l/Bound-Supply]} [supply node] :literal [supply node] :local [supply node] @@ -81,54 +46,52 @@ :label [supply node] :goto [supply node] :native [supply node] - :dispatch (let [[supply value] (parallel supply (:value node))] + :dispatch (let [[supply value] (lower supply (:value node))] [supply {:op :dispatch :value value :spec (:spec node)}]) - :case (let [[supply selector] (parallel supply (:selector node)) - [supply arms] (parallel-forms supply (:arms node))] + :case (let [[supply selector] (lower supply (:selector node)) + [supply arms] (lower-forms supply (:arms node))] [supply {:op :case :selector selector :arms arms}]) - :recur (let [[supply args] (parallel-forms supply (:args node))] + :recur (let [[supply args] (lower-forms supply (:args node))] [supply {:op :recur :target (:target node) :args args}]) - :loop (let [[supply bindings] (parallel-bindings supply (:bindings node)) - [supply body] (parallel supply (:body node))] + :loop (let [[supply bindings] (lower-bindings supply (:bindings node)) + [supply body] (lower supply (:body node))] [supply {:op :loop :id (:id node) :bindings bindings :body body}]) - :if (let [[supply test] (parallel supply (:test node)) - [supply yes] (parallel supply (:then node)) - [supply no] (parallel supply (:else node))] + :if (let [[supply test] (lower supply (:test node)) + [supply yes] (lower supply (:then node)) + [supply no] (lower supply (:else node))] [supply {:op :if :test test :then yes :else no}]) - :throw (let [[supply value] (parallel supply (:value node))] [supply {:op :throw :value value}]) - :write (let [[supply value] (parallel supply (:value node))] - [supply {:op :write :target (:target node) :value value}]) - :do (let [[supply forms] (parallel-forms supply (:forms node))] [supply {:op :do :forms forms}]) - :host (let [[supply args] (parallel-forms supply (:args node))] + :throw (let [[supply value] (lower supply (:value node))] [supply {:op :throw :value value}]) + :do (let [[supply forms] (lower-forms supply (:forms node))] [supply {:op :do :forms forms}]) + :host (let [[supply args] (lower-forms supply (:args node))] [supply {:op :host :operation (:operation node) :args args}]) - :invoke (let [[supply function] (parallel supply (:function node)) - [supply args] (parallel-forms supply (:args node))] + :invoke (let [[supply function] (lower supply (:function node)) + [supply args] (lower-forms supply (:args node))] [supply {:op :invoke :function function :args args}]) - :collection (let [[supply items] (parallel-forms supply (:items node)) - [supply metadata] (if-let [metadata (:metadata node)] (parallel supply metadata) [supply nil])] + :collection (let [[supply items] (lower-forms supply (:items node)) + [supply metadata] (if-let [metadata (:metadata node)] (lower supply metadata) [supply nil])] [supply {:op :collection :kind (:kind node) :items items :metadata metadata}]) - :let (let [[supply bindings] (parallel-bindings supply (:bindings node)) - [supply body] (parallel supply (:body node))] + :let (let [[supply bindings] (lower-bindings supply (:bindings node)) + [supply body] (lower supply (:body node))] [supply {:op :let :bindings bindings :body body}]) - :psetq (let [[supply bindings writes] (save-parallel-values supply (:pairs node)) + :setq (let [[supply values] (lower-forms supply (mapv :value (:pairs node)))] + [supply {:op :do :forms + (mapv (fn [pair value] + (assoc value :node {:op :write :target (:target pair) :value value})) + (:pairs node) values)}]) + :psetq (let [[supply bindings writes] (save-values supply (:pairs node)) template (first writes)] (if template [supply {:op :let :bindings bindings :body (assoc template :node {:op :do :forms (conj writes (assoc template :node {:op :literal :value nil}))})}] [supply {:op :literal :value nil}]))) - (parallel - {:from [l/Sequential Expr] :to (t/HVec [l/Bound-Supply l/Assigned-Expr]) + (lower + {:from [l/Classified Expr] :to (t/HVec [l/Bound-Supply l/Assigned-Expr]) :args [l/Bound-Supply] :body true} [supply expr] - (let [[supply node] (parallel-node supply (:node expr))] [supply (assoc expr :node node)]))) - -(np/defpass sequential-stage - {:from [l/Classified Compilation] :to [l/Sequential Compilation] :body true} - [compilation] - (pipeline/map-value sequential compilation)) + (let [[supply node] (lower-node supply (:node expr))] [supply (assoc expr :node node)]))) -(np/defpass parallel-stage - {:from [l/Sequential Compilation] :to [l/Assigned Compilation] :body true} +(np/defpass stage + {:from [l/Classified Compilation] :to [l/Assigned Compilation] :body true} [compilation] - (apply parallel compilation)) + (apply lower compilation)) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/collections.clj b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj index 5029433..e21d2d7 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/collections.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/collections.clj @@ -38,6 +38,14 @@ (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}} diff --git a/src/com/tailrecursion/blacktide/compiler/passes/emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/emit.clj index 7960dac..5b850b8 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/emit.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/emit.clj @@ -23,10 +23,7 @@ :native (native/form #{} (:value node)) :dispatch (cases/selector-form (host/typed-form (:jvm (:value node)) (atom-form (:value (:value node)))) (:spec node)) :construct (collections/form (mapv atom-form (:items node)) (:unique? node) (:kind node)) - :metadata (let [value (gensym "value__") metadata (gensym "metadata__")] - (list 'let* [(with-meta value {:tag 'clojure.lang.IObj}) (atom-form (:value node)) - (with-meta metadata {:tag 'clojure.lang.IPersistentMap}) (atom-form (:metadata node))] - (list '. value 'withMeta metadata))) + :metadata (collections/metadata-form (atom-form (:value node)) (atom-form (:metadata node))) :convert (host/conversion-form (:conversion node) (:jvm (:value node)) (atom-form (:value (:value node)))) :host (host/host-form (mapv (fn [arg] (host/typed-form (assoc (:jvm arg) :primitive? false) (atom-form (:value arg)))) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj b/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj index 43d29ff..851c8d4 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/invariants.clj @@ -17,12 +17,7 @@ (np/defpass expression {:from [l/Joined Expr] :to [l/Joined Expr] :args [(t/Set t/Sym)]} [invariants node] - :bind (assoc node :body (expression invariants (:body node))) - :join (assoc node :value (expression invariants (:value node)) :body (expression invariants (:body node))) - :branch (assoc node :yes (expression invariants (:yes node)) :no (expression invariants (:no node))) - :switch (assoc node :arms (mapv #(expression invariants %) (:arms node))) :loop (let [invariants (set/difference (set (:slots node)) (assigned (:body node)))] (assoc node :slots (filterv #(not (contains? invariants %)) (:slots node)) :body (expression invariants (:body node)))) - :recur (assoc node :slots (filterv #(not (contains? invariants %)) (:slots node))) - :return node :throw node) + :recur (assoc node :slots (filterv #(not (contains? invariants %)) (:slots node)))) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/source.clj b/src/com/tailrecursion/blacktide/compiler/passes/source.clj index 50cbb46..448bf47 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/source.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/source.clj @@ -70,12 +70,6 @@ :macro-env (assoc (:macro-env ctx) name {:name name :tag tag})) local]) (errors/fail :invalid-binding "Binding targets must be simple symbols" {:binding name} (:site ctx)))) -(np/defpass case-expression - {:from [l/Bound Exprs] :to (t/HVec [l/Bound-Supply l/Bound-Expr]) - :args [l/Bound-Supply l/Bound-Context l/Bound-Expr] :body true} - [supply ctx selector arms] - [supply (expression ctx {:hint nil :var-meta {}} {:op :case :selector selector :arms arms})]) - (np/defpass protocol-argument {:from [l/Bound Expr] :to (t/HVec [l/Bound-Supply l/Bound-Expr]) :args [l/Bound-Supply l/Bound-Context l/Bound-Expr java.lang.Class] :body true} @@ -238,7 +232,7 @@ (let [[supply inner bindings] (parse-bindings supply ctx :immutable (second selector)) [supply selector] (parse-operand supply inner (first (nnext selector))) [supply arms] (parse-arms supply inner arms) - [supply body] (case-expression supply ctx selector arms)] + body (expression ctx {:hint nil :var-meta {}} {:op :case :selector selector :arms arms})] [supply (expression ctx {:hint nil :var-meta {}} {:op :let :bindings bindings :body body})]))))) (parse-dispatch @@ -251,7 +245,7 @@ (errors/fail :invalid-case "case* dispatch must reference a lexical local" {} (:site ctx))) (let [selector (expression ctx {:hint nil :var-meta {}} {:op :dispatch :value value :spec spec}) [supply arms] (parse-arms supply ctx arms)] - (case-expression supply ctx selector arms)))) + [supply (expression ctx {:hint nil :var-meta {}} {:op :case :selector selector :arms arms})]))) (parse-call {:from [l/Bound Forms] :to (t/HVec [l/Bound-Supply l/Bound-Expr]) @@ -341,7 +335,7 @@ (errors/fail :invalid-var "var requires a resolvable Var name" {} (:site ctx)))) (#{'fn* 'letfn* 'try 'set! 'monitor-enter 'monitor-exit} op) (errors/fail :unsupported-form "Form is unsupported inside loophole; place it outside the region" {:operator op} (:site ctx)) - (#{'new '.} op) (parse-host supply ctx (or (host/syntax ctx forms) + (#{'new '.} op) (parse-host supply ctx (or (host/syntax forms) (errors/fail :invalid-interop "Missing host syntax" {} (:site ctx)))) (= op 'do) (parse-body supply ctx (vec (rest forms))) (= op 'let*) (parse-scope supply ctx false forms) @@ -378,7 +372,7 @@ expanded (if (instance? clojure.lang.IObj expanded) (with-meta expanded (merge (meta expanded) (meta form))) expanded)] (parse supply ctx expanded)) - :else (if-let [syntax (host/syntax ctx forms)] + :else (if-let [syntax (host/syntax forms)] (parse-host supply ctx syntax) (parse-call supply ctx forms))))) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj b/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj index 431e186..1cdfb88 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/source_host.clj @@ -1,5 +1,5 @@ (ns ^:typed.clojure com.tailrecursion.blacktide.compiler.passes.source-host - "Recognize native JVM syntax with the source lexical environment. Return host + "Recognize native JVM syntax in the caller namespace. Return host descriptors and ordered source operands; none of those operands is evaluated." (:require [com.tailrecursion.nanopass :as np] [com.tailrecursion.blacktide.compiler.languages :as l] @@ -27,8 +27,8 @@ :else {:op :static :class class :member (clojure.core/symbol member) :tags tags}))))) (np/defpass syntax - {:from [l/Bound Forms] :to (t/Option l/JVM-Syntax) :args [l/Bound-Context] :body true} - [ctx forms] + {:from [l/Bound Forms] :to (t/Option l/JVM-Syntax) :body true} + [forms] (let [op (first forms) args (subvec forms 1)] (cond (= op 'new) @@ -60,9 +60,9 @@ target (if (jvm/resolved-class target) (with-meta (list 'clojure.core/identity target) {:tag 'java.lang.Class}) target)] (when (empty? args) (throw (IllegalArgumentException. "Instance member requires a receiver"))) - (syntax ctx (into ['. target (symbol (subs (name op) 1))] (rest args)))) + (syntax (into ['. target (symbol (subs (name op) 1))] (rest args)))) (.endsWith (name op) ".") - (syntax ctx (into ['new (symbol (subs (str op) 0 (dec (count (str op)))))] args)) + (syntax (into ['new (symbol (subs (str op) 0 (dec (count (str op)))))] args)) :else (when-let [operation (qualified op)] (when (and (= :instance (:op operation)) (empty? args)) (throw (IllegalArgumentException. "Qualified instance call requires a receiver"))) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj b/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj index 723f7ce..0cc1943 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/structured_emit.clj @@ -53,10 +53,7 @@ :value (atom-form types (:value rhs)) :global (emit/rhs-form rhs) :external (emit/rhs-form rhs) :var (emit/rhs-form rhs) :native (emit/rhs-form rhs) :construct (collections/form (mapv #(atom-form types %) (:items rhs)) (:unique? rhs) (:kind rhs)) - :metadata (let [value (gensym "value__") metadata (gensym "metadata__")] - (list 'let* [(with-meta value {:tag 'clojure.lang.IObj}) (atom-form types (:value rhs)) - (with-meta metadata {:tag 'clojure.lang.IPersistentMap}) (atom-form types (:metadata rhs))] - (list '. value 'withMeta metadata))) + :metadata (collections/metadata-form (atom-form types (:value rhs)) (atom-form types (:metadata rhs))) :invoke (list* (atom-form types (:function rhs)) (mapv #(atom-form types %) (:args rhs))))) (np/defpass rhs-type diff --git a/test/com/tailrecursion/blacktide/check.clj b/test/com/tailrecursion/blacktide/check.clj index 6c6553c..4aab17c 100644 --- a/test/com/tailrecursion/blacktide/check.clj +++ b/test/com/tailrecursion/blacktide/check.clj @@ -15,9 +15,9 @@ (deftest compilation-stage-contracts (doseq [[accepted stages] - [[true `[boundaries/stage assignment/sequential-stage assignment/parallel-stage]] - [false `[boundaries/stage assignment/parallel-stage assignment/sequential-stage]] - [false `[boundaries/stage assignment/sequential-stage]]]] + [[true `[boundaries/stage assignment/stage]] + [false `[assignment/stage boundaries/stage]] + [false `[boundaries/stage]]]] (let [form `(fn [compilation#] (-> compilation# ~@stages)) {:keys [ex delayed-errors]} (probes/check-form form `[l/Bound-Compilation :-> l/Assigned-Compilation]) @@ -32,8 +32,8 @@ batch {:op :psetq :pairs []} assigned 'com.tailrecursion.blacktide.compiler.languages/Assigned-Expr] (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Bound-Node {:op :setq :pairs []} true) - (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Sequential-Node {:op :setq :pairs []} false) - (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Sequential-Node batch true) + (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Assigned-Node {:op :setq :pairs []} false) + (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Bound-Node batch true) (probes/accepts? assigned (expr batch) false) (probes/accepts? assigned (expr {:op :do :forms [(expr batch)]}) false) (probes/accepts? 'com.tailrecursion.blacktide.compiler.languages/Native-Expr