mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-06 13:07:48 +00:00
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:
@@ -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.
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
---------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 _
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
errormsg: "undeclared identifier: '_'"
|
||||
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
"""
|
||||
|
||||
iterator iter(): (int, int) =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
errormsg: "undeclared identifier: '_'"
|
||||
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
"""
|
||||
|
||||
for _ in ["a"]:
|
||||
|
||||
4
tests/stmt/tgenericsunderscore.nim
Normal file
4
tests/stmt/tgenericsunderscore.nim
Normal 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]#
|
||||
15
tests/stmt/tmiscunderscore.nim
Normal file
15
tests/stmt/tmiscunderscore.nim
Normal 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)
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
errormsg: "undeclared identifier: '_'"
|
||||
errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
"""
|
||||
|
||||
# issue #12094, #13804
|
||||
|
||||
Reference in New Issue
Block a user