document general use of _, error message, fixes (#21584)

* document general use of `_`, error message, fixes

fixes #20687, fixes #21435

Documentation and changelog updated to clarify new universal behavior
of `_`. Also new error message for attempting to use `_`, new tests,
and fixes with overloadable symbols and
implicit generics.

* add test for #21435
This commit is contained in:
metagn
2023-03-30 16:34:42 +03:00
committed by GitHub
parent 51ced0d684
commit ecf9efa397
11 changed files with 100 additions and 31 deletions

View File

@@ -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.

View File

@@ -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 =

View File

@@ -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)

View File

@@ -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
---------------

View File

@@ -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

View File

@@ -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 _
echo _

View File

@@ -1,5 +1,5 @@
discard """
errormsg: "undeclared identifier: '_'"
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
"""
iterator iter(): (int, int) =

View File

@@ -1,5 +1,5 @@
discard """
errormsg: "undeclared identifier: '_'"
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
"""
for _ in ["a"]:

View File

@@ -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]#

View File

@@ -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)

View File

@@ -1,5 +1,5 @@
discard """
errormsg: "undeclared identifier: '_'"
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
"""
# issue #12094, #13804