summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJudd <foldl@outlook.com>2020-04-21 20:50:16 +0800
committerGitHub <noreply@github.com>2020-04-21 14:50:16 +0200
commit04c326569bdafa619d6596d2d674d3acbcc1ecf3 (patch)
tree82bc02bdb92dc2a60e9d5a3164489f0d5580b3d5
parent1a44b7e3ced0e2d092f2c607d61a6b28c1ca339c (diff)
downloadNim-04c326569bdafa619d6596d2d674d3acbcc1ecf3.tar.gz
fix mapIt issues #12625 & #12639 (#14041)
* fix mapIt issues #12625 & #12639:

1. fallback to call `map` when the result of `op` is a closure;
2. use `items(s)` in the for loop.

* fix test errors.

* add comments and InType is moved.

* fix ident.
-rw-r--r--lib/pure/collections/sequtils.nim50
-rw-r--r--tests/collections/tseq.nim3
2 files changed, 39 insertions, 14 deletions
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index ea02ff1fd..02d48973e 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -924,24 +924,46 @@ template mapIt*(s: typed, op: untyped): untyped =
       block:
         var it{.inject.}: type(items(s));
         op))
-  when compiles(s.len):
-    block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
+  when OutType is not (proc):
+    # Here, we avoid to create closures in loops.
+    # This avoids https://github.com/nim-lang/Nim/issues/12625
+    when compiles(s.len):
+      block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
 
-      # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
-      # (`error: use of undeclared identifier`) instead of Nim compile errors
-      evalOnceAs(s2, s, compiles((let _ = s)))
+        # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
+        # (`error: use of undeclared identifier`) instead of Nim compile errors
+        evalOnceAs(s2, s, compiles((let _ = s)))
 
-      var i = 0
-      var result = newSeq[OutType](s2.len)
-      for it {.inject.} in s2:
-        result[i] = op
-        i += 1
+        var i = 0
+        var result = newSeq[OutType](s2.len)
+        for it {.inject.} in s2:
+          result[i] = op
+          i += 1
+        result
+    else:
+      var result: seq[OutType] = @[]
+      # use `items` to avoid https://github.com/nim-lang/Nim/issues/12639
+      for it {.inject.} in items(s):
+        result.add(op)
       result
   else:
-    var result: seq[OutType] = @[]
-    for it {.inject.} in s:
-      result.add(op)
-    result
+    # `op` is going to create closures in loops, let's fallback to `map`.
+    # NOTE: Without this fallback, developers have to define a helper function and
+    # call `map`:
+    #   [1, 2].map((it) => ((x: int) => it + x))
+    # With this fallback, above code can be simplified to:
+    #   [1, 2].mapIt((x: int) => it + x)
+    # In this case, `mapIt` is just syntax sugar for `map`.
+
+    when defined(nimHasTypeof):
+      type InType = typeof(items(s), typeOfIter)
+    else:
+      type InType = type(items(s))
+    # Use a help proc `f` to create closures for each element in `s`
+    let f = proc (x: InType): OutType =
+              let it {.inject.} = x
+              op
+    map(s, f)
 
 template applyIt*(varSeq, op: untyped) =
   ## Convenience template around the mutable ``apply`` proc to reduce typing.
diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim
index 6a28bc8e6..ac68e5636 100644
--- a/tests/collections/tseq.nim
+++ b/tests/collections/tseq.nim
@@ -204,3 +204,6 @@ block ttoseq:
   var y: type("a b c".split)
   y = "xzy"
   stdout.write("\n")
+
+block tseqmapitchain:
+  doAssert @[101, 102] == [1, 2].mapIt(func (x: int): int = it + x).mapIt(it(100))
\ No newline at end of file