Skip to content

Commit ba5e70a

Browse files
mfikesswannodette
authored andcommitted
CLJS-2085: defrecord recur method head target object
1 parent 9f6e53d commit ba5e70a

4 files changed

Lines changed: 87 additions & 5 deletions

File tree

src/main/clojure/cljs/analyzer.cljc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
:protocol-duped-method true
138138
:protocol-multiple-impls true
139139
:protocol-with-variadic-method true
140+
:protocol-impl-recur-with-target true
140141
:single-segment-namespace true
141142
:munged-namespace true
142143
:ns-var-clash true
@@ -358,6 +359,10 @@
358359
(str "Protocol " (:protocol info) " declares method "
359360
(:name info) " with variadic signature (&)"))
360361

362+
(defmethod error-message :protocol-impl-recur-with-target
363+
[warning-type info]
364+
(str "Ignoring target object \"" (:form info) "\" passed in recur to protocol method head"))
365+
361366
(defmethod error-message :multiple-variadic-overloads
362367
[warning-type info]
363368
(str (:name info) ": Can't have more than 1 variadic overload"))
@@ -1471,7 +1476,9 @@
14711476
(butlast params)
14721477
params)
14731478
fixed-arity (count params')
1474-
recur-frame {:params params :flag (atom nil)}
1479+
recur-frame {:protocol-impl (:protocol-impl env)
1480+
:params params
1481+
:flag (atom nil)}
14751482
recur-frames (cons recur-frame *recur-frames*)
14761483
body-env (assoc env :context :return :locals locals)
14771484
body-form `(do ~@body)
@@ -1758,11 +1765,18 @@
17581765
[op env [_ & exprs :as form] _ _]
17591766
(let [context (:context env)
17601767
frame (first *recur-frames*)
1768+
;; Add dummy implicit target object if recuring to proto impl method head
1769+
add-implicit-target-object? (and (:protocol-impl frame)
1770+
(= (count exprs) (dec (count (:params frame)))))
1771+
exprs (cond->> exprs add-implicit-target-object? (cons nil))
17611772
exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))]
17621773
(when-not frame
17631774
(throw (error env "Can't recur here")))
17641775
(when-not (= (count exprs) (count (:params frame)))
17651776
(throw (error env "recur argument count mismatch")))
1777+
(when (and (:protocol-impl frame)
1778+
(not add-implicit-target-object?))
1779+
(warning :protocol-impl-recur-with-target env {:form (:form (first exprs))}))
17661780
(reset! (:flag frame) true)
17671781
(assoc {:env env :op :recur :form form}
17681782
:frame frame

src/test/cljs/cljs/recur_test.cljs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
;; Copyright (c) Rich Hickey. All rights reserved.
2+
;; The use and distribution terms for this software are covered by the
3+
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
;; which can be found in the file epl-v10.html at the root of this distribution.
5+
;; By using this software in any fashion, you are agreeing to be bound by
6+
;; the terms of this license.
7+
;; You must not remove this notice, or any other, from this software.
8+
9+
(ns cljs.recur-test
10+
(:require [cljs.test :refer-macros [deftest is]]))
11+
12+
;; Setup for CLJS-2085
13+
14+
(defprotocol ISearch
15+
(search [this coll]))
16+
17+
;; Passing this in the recur call here will cause a warning to be emitted
18+
(defrecord Search1 [needle]
19+
ISearch
20+
(search [this coll]
21+
(when (seq coll)
22+
(if (= needle (first coll))
23+
needle
24+
(recur this (rest coll))))))
25+
26+
;; This code will be accepted as is
27+
(defrecord Search2 [needle]
28+
ISearch
29+
(search [_ coll]
30+
(when (seq coll)
31+
(if (= needle (first coll))
32+
needle
33+
(recur (rest coll))))))
34+
35+
;; This code will also be accepted as is; the recur is to a loop
36+
(defrecord Search3 [needle]
37+
ISearch
38+
(search [this coll]
39+
(loop [coll coll]
40+
(when (seq coll)
41+
(if (= needle (first coll))
42+
needle
43+
(recur (rest coll)))))))
44+
45+
;; This code should not cause a warning to be emitted
46+
(defrecord Search4 [needle]
47+
ISearch
48+
(search [this coll]
49+
(let [search-fn (fn [coll]
50+
(when (seq coll)
51+
(if (= needle (first coll))
52+
needle
53+
(recur (rest coll)))))]
54+
(search-fn coll))))
55+
56+
(deftest cljs-2085-test
57+
(is (= 1 (-> (->Search1 1) (search [:a 1 "b"]))))
58+
(is (nil? (-> (->Search1 :z) (search [:a 1 "b"]))))
59+
(is (= 1 (-> (->Search2 1) (search [:a 1 "b"]))))
60+
(is (nil? (-> (->Search2 :z) (search [:a 1 "b"]))))
61+
(is (= 1 (-> (->Search3 1) (search [:a 1 "b"]))))
62+
(is (nil? (-> (->Search3 :z) (search [:a 1 "b"]))))
63+
(is (= 1 (-> (->Search4 1) (search [:a 1 "b"]))))
64+
(is (nil? (-> (->Search4 :z) (search [:a 1 "b"])))))

src/test/cljs/test_runner.cljs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
[cljs.predicates-test]
4242
[cljs.tagged-literals-test]
4343
[cljs.test-test]
44-
[static.core-test]))
44+
[static.core-test]
45+
[cljs.recur-test]))
4546

4647
(set! *print-newline* false)
4748
(set-print-fn! js/print)
@@ -80,4 +81,5 @@
8081
'cljs.syntax-quote-test
8182
'cljs.tagged-literals-test
8283
'cljs.test-test
83-
'static.core-test)
84+
'static.core-test
85+
'cljs.recur-test)

src/test/self/self_parity/test.cljs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@
296296
[cljs.syntax-quote-test]
297297
[cljs.predicates-test]
298298
[cljs.test-test]
299-
[static.core-test]))
299+
[static.core-test]
300+
[cljs.recur-test]))
300301
(fn [{:keys [value error]}]
301302
(if error
302303
(handle-error error (:source-maps @st))
@@ -334,7 +335,8 @@
334335
'cljs.syntax-quote-test
335336
'cljs.predicates-test
336337
'cljs.test-test
337-
'static.core-test)
338+
'static.core-test
339+
'cljs.recur-test)
338340
(fn [{:keys [value error]}]
339341
(when error
340342
(handle-error error (:source-maps @st))))))))))

0 commit comments

Comments
 (0)