summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2023-01-08 08:49:49 +0300
committerGitHub <noreply@github.com>2023-01-08 06:49:49 +0100
commit05c6db6500de0551882a2d6cc612c9afb6ca674c (patch)
tree2b7492ae69524ed95384a160d048a573f969cf84
parentb34412ff0fb450278d8c93f6e5d7bf0e8d3f0a0b (diff)
downloadNim-05c6db6500de0551882a2d6cc612c9afb6ca674c.tar.gz
underscores for routine parameters (#21192)
* underscores for routine parameters

fixes #13443, fixes #13804, refs #21121

* add changelog + more tests

* support generics and ensure inferred lambdas work
-rw-r--r--changelogs/changelog_2_0_0.md15
-rw-r--r--compiler/lookups.nim1
-rw-r--r--compiler/semtempl.nim5
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--tests/proc/tunderscoreparam.nim109
5 files changed, 131 insertions, 3 deletions
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
index 9ff493b54..c8427cc2a 100644
--- a/changelogs/changelog_2_0_0.md
+++ b/changelogs/changelog_2_0_0.md
@@ -135,6 +135,21 @@
 
 - The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
 
+- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body.
+  The following code now does not compile:
+
+  ```nim
+  proc foo(_: int): int = _ + 1
+  echo foo(1)
+  ```
+
+  Instead, the following code now compiles:
+
+  ```nim
+  proc foo(_, _: int): int = 123
+  echo foo(1, 2)
+  ```
+
 - - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
 
 ## Standard library additions and changes
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index d50fc6e27..3f4fcb4d0 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -323,6 +323,7 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
 # xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
 # proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
 proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
+  if sym.name.s == "_": return
   let conflict = scope.addUniqueSym(sym)
   if conflict != nil:
     if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 0016e9edf..3b2f2dd9e 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -642,8 +642,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     # body by the absence of the sfGenSym flag:
     for i in 1..<s.typ.n.len:
       let param = s.typ.n[i].sym
-      param.flags.incl sfTemplateParam
-      param.flags.excl sfGenSym
+      if param.name.s != "_":
+        param.flags.incl sfTemplateParam
+        param.flags.excl sfGenSym
       if param.typ.kind != tyUntyped: allUntyped = false
   else:
     s.typ = newTypeS(tyProc, c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index a8b147eef..844bc2070 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1372,7 +1372,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       inc(counter)
       if def != nil and def.kind != nkEmpty:
         arg.ast = copyTree(def)
-      if containsOrIncl(check, arg.name.id):
+      if arg.name.s == "_":
+        arg.flags.incl(sfGenSym)
+      elif containsOrIncl(check, arg.name.id):
         localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
       result.n.add newSymNode(arg)
       rawAddSon(result, finalType)
diff --git a/tests/proc/tunderscoreparam.nim b/tests/proc/tunderscoreparam.nim
new file mode 100644
index 000000000..c2f6cf8ad
--- /dev/null
+++ b/tests/proc/tunderscoreparam.nim
@@ -0,0 +1,109 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/[assertions, sequtils]
+
+proc test() =
+  block:
+    proc ok(_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doassert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+    
+  block:
+    proc ok[T](_, _, a: T): T =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+    doAssert ok("a", "b", "c") == "c"
+    doAssert not compiles(ok(1, 2, "a"))
+  
+  block:
+    let ok = proc (_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+  
+  block:
+    proc foo(lam: proc (_, _, a: int): int): int =
+      lam(4, 2, 5)
+    doAssert foo(proc (_, _, a: auto): auto =
+      doAssert not compiles(_)
+      a) == 5
+    
+  block:
+    iterator fn(_, _: int, c: int): int = yield c
+    doAssert toSeq(fn(1,2,3)) == @[3]
+
+  block:
+    template ok(_, _, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    doAssert not (compiles do:
+      template bad(_: int): int = _
+      discard bad(3))
+
+  block:
+    template ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    template ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    template ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+  
+  block:
+    template main2() =
+      iterator fn(_, _: int, c: int): int = yield c
+    main2()
+
+  block:
+    template main =
+      proc foo(_: int) =
+        let a = _
+    doAssert not compiles(main())
+
+proc closureTest() =
+  var x = 0
+
+  block:
+    proc foo(_, _: int) = x += 5
+
+    foo(1, 2)
+    doAssert x == 5
+
+  block:
+    proc foo(_: int, _: float) = x += 5
+
+    foo(1, 2)
+    doAssert x == 10
+
+  block:
+    proc foo(_: int, _: float, _: string) = x += 5
+
+    foo(1, 2, "5")
+    doAssert x == 15
+
+static: test()
+test()
+
+when not defined(js):
+  static: closureTest()
+closureTest()