summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelogs/changelog_2_0_0.md36
-rw-r--r--compiler/lookups.nim27
-rw-r--r--compiler/semtypes.nim13
-rw-r--r--doc/manual.md13
-rw-r--r--tests/proc/tunderscoreparam.nim13
-rw-r--r--tests/stmt/tforloop_tuple_multiple_underscore.nim4
-rw-r--r--tests/stmt/tforloop_tuple_underscore.nim2
-rw-r--r--tests/stmt/tforloop_underscore.nim2
-rw-r--r--tests/stmt/tgenericsunderscore.nim4
-rw-r--r--tests/stmt/tmiscunderscore.nim15
-rw-r--r--tests/template/tunderscore1.nim2
11 files changed, 100 insertions, 31 deletions
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
index 19d97b769..a82ce149b 100644
--- a/changelogs/changelog_2_0_0.md
+++ b/changelogs/changelog_2_0_0.md
@@ -135,26 +135,42 @@
 
 - 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:
+- The underscore identifier (`_`) is now generally not added to scope when
+  used as the name of a definition. While this was already the case for
+  variables, it is now also the case for routine parameters, generic
+  parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
 
   ```nim
   proc foo(_: int): int = _ + 1
   echo foo(1)
+
+  proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+  echo foo[int]()
+
+  proc _() = echo "_"
+  _()
+
+  type _ = int
+  let x: _ = 3
   ```
 
-  Instead, the following code now compiles:
+  Whereas the following code now compiles:
 
   ```nim
   proc foo(_, _: int): int = 123
   echo foo(1, 2)
-  ```
-- Underscores (`_`) as generic parameters are not supported and cannot be used.
-  Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else:
-  
-  ```nim
-  proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile
-  proc foo[T](t: typedesc[T]): string = "BAR" # Can compile
+
+  proc foo[_, _](): int = 123
+  echo foo[int, bool]()
+
+  proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
+  echo foo(int, bool)
+
+  proc _() = echo "one"
+  proc _() = echo "two"
+
+  type _ = int
+  type _ = float
   ```
 
 - - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 3f028a52f..fc84b9051 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -396,11 +396,12 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
   if fn.kind notin OverloadableSyms:
     internalError(c.config, fn.info, "addOverloadableSymAt")
     return
-  let check = strTableGet(scope.symbols, fn.name)
-  if check != nil and check.kind notin OverloadableSyms:
-    wrongRedefinition(c, fn.info, fn.name.s, check.info)
-  else:
-    scope.addSym(fn)
+  if fn.name.s != "_":
+    let check = strTableGet(scope.symbols, fn.name)
+    if check != nil and check.kind notin OverloadableSyms:
+      wrongRedefinition(c, fn.info, fn.name.s, check.info)
+    else:
+      scope.addSym(fn)
 
 proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
   ## adds an overloadable symbol on the scope and the interface if appropriate
@@ -546,12 +547,16 @@ proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
   errorUseQualifier(c, info, candidates, prefix)
 
 proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
-  var err = "undeclared identifier: '" & name & "'" & extra
-  if c.recursiveDep.len > 0:
-    err.add "\nThis might be caused by a recursive module dependency:\n"
-    err.add c.recursiveDep
-    # prevent excessive errors for 'nim check'
-    c.recursiveDep = ""
+  var err: string
+  if name == "_":
+    err = "the special identifier '_' is ignored in declarations and cannot be used"
+  else:
+    err = "undeclared identifier: '" & name & "'" & extra
+    if c.recursiveDep.len > 0:
+      err.add "\nThis might be caused by a recursive module dependency:\n"
+      err.add c.recursiveDep
+      # prevent excessive errors for 'nim check'
+      c.recursiveDep = ""
   localError(c.config, info, errGenerated, err)
 
 proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 2f485d65b..fbba4f36f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1359,6 +1359,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
 
     for j in 0..<a.len-2:
       var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
+      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 & "'")
       if a[j].kind == nkPragmaExpr:
         pragma(c, arg, a[j][1], paramPragmas)
       if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
@@ -1367,8 +1371,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         else:
           localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s)
           typ = errorType(c)
+      var nameForLift = arg.name.s
+      if sfGenSym in arg.flags:
+        nameForLift.add("`gensym" & $arg.id)
       let lifted = liftParamType(c, kind, genericParams, typ,
-                                 arg.name.s, arg.info)
+                                 nameForLift, arg.info)
       let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
       arg.typ = finalType
       arg.position = counter
@@ -1376,10 +1383,6 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       inc(counter)
       if def != nil and def.kind != nkEmpty:
         arg.ast = copyTree(def)
-      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)
       addParamOrResult(c, arg, kind)
diff --git a/doc/manual.md b/doc/manual.md
index 1fd35e7b3..850a04b0d 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -3076,6 +3076,19 @@ when they are declared. The only exception to this is if the `{.importc.}`
 pragma (or any of the other `importX` pragmas) is applied, in this case the
 value is expected to come from native code, typically a C/C++ `const`.
 
+Special identifier `_` (underscore)
+-----------------------------------
+
+The identifier `_` has a special meaning in declarations.
+Any definition with the name `_` will not be added to scope, meaning the
+definition is evaluated, but cannot be used. As a result the name `_` can be
+indefinitely redefined.
+
+  ```nim
+  let _ = 123
+  echo _ # error
+  let _ = 456 # compiles
+  ```
 
 Tuple unpacking
 ---------------
diff --git a/tests/proc/tunderscoreparam.nim b/tests/proc/tunderscoreparam.nim
index c2f6cf8ad..8d60603f1 100644
--- a/tests/proc/tunderscoreparam.nim
+++ b/tests/proc/tunderscoreparam.nim
@@ -79,6 +79,19 @@ proc test() =
       proc foo(_: int) =
         let a = _
     doAssert not compiles(main())
+  
+  block: # generic params
+    doAssert not (compiles do:
+      proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+      doAssert foo[int]() == 0)
+  
+  block:
+    proc foo[_, _](): int = 123
+    doAssert foo[int, bool]() == 123
+  
+  block:
+    proc foo[T; U](_: typedesc[T]; _: typedesc[U]): (T, U) = (default(T), default(U))
+    doAssert foo(int, bool) == (0, false)
 
 proc closureTest() =
   var x = 0
diff --git a/tests/stmt/tforloop_tuple_multiple_underscore.nim b/tests/stmt/tforloop_tuple_multiple_underscore.nim
index 2afdb0b77..96804df18 100644
--- a/tests/stmt/tforloop_tuple_multiple_underscore.nim
+++ b/tests/stmt/tforloop_tuple_multiple_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 iterator iter(): (int, int, int) =
@@ -7,4 +7,4 @@ iterator iter(): (int, int, int) =
 
 
 for (_, i, _) in iter():
-  echo _
\ No newline at end of file
+  echo _
diff --git a/tests/stmt/tforloop_tuple_underscore.nim b/tests/stmt/tforloop_tuple_underscore.nim
index 8cbb0fc10..eda42d527 100644
--- a/tests/stmt/tforloop_tuple_underscore.nim
+++ b/tests/stmt/tforloop_tuple_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 iterator iter(): (int, int) =
diff --git a/tests/stmt/tforloop_underscore.nim b/tests/stmt/tforloop_underscore.nim
index c5b49da77..ce1c86386 100644
--- a/tests/stmt/tforloop_underscore.nim
+++ b/tests/stmt/tforloop_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 for _ in ["a"]:
diff --git a/tests/stmt/tgenericsunderscore.nim b/tests/stmt/tgenericsunderscore.nim
new file mode 100644
index 000000000..be2b8ec78
--- /dev/null
+++ b/tests/stmt/tgenericsunderscore.nim
@@ -0,0 +1,4 @@
+# issue #21435
+
+proc foo[_](x: typedesc[_]): string = "BAR" #[tt.Error
+                        ^ the special identifier '_' is ignored in declarations and cannot be used]#
diff --git a/tests/stmt/tmiscunderscore.nim b/tests/stmt/tmiscunderscore.nim
new file mode 100644
index 000000000..c4bae1c3d
--- /dev/null
+++ b/tests/stmt/tmiscunderscore.nim
@@ -0,0 +1,15 @@
+import std/assertions
+
+block:
+  proc _() = echo "one"
+  doAssert not compiles(_())
+  proc _() = echo "two"
+  doAssert not compiles(_())
+
+block:
+  type _ = int
+  doAssert not (compiles do:
+    let x: _ = 3)
+  type _ = float
+  doAssert not (compiles do:
+    let x: _ = 3)
diff --git a/tests/template/tunderscore1.nim b/tests/template/tunderscore1.nim
index 71af39501..d74e5ba63 100644
--- a/tests/template/tunderscore1.nim
+++ b/tests/template/tunderscore1.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 # issue #12094, #13804