diff options
-rw-r--r-- | changelogs/changelog_2_0_0.md | 36 | ||||
-rw-r--r-- | compiler/lookups.nim | 27 | ||||
-rw-r--r-- | compiler/semtypes.nim | 13 | ||||
-rw-r--r-- | doc/manual.md | 13 | ||||
-rw-r--r-- | tests/proc/tunderscoreparam.nim | 13 | ||||
-rw-r--r-- | tests/stmt/tforloop_tuple_multiple_underscore.nim | 4 | ||||
-rw-r--r-- | tests/stmt/tforloop_tuple_underscore.nim | 2 | ||||
-rw-r--r-- | tests/stmt/tforloop_underscore.nim | 2 | ||||
-rw-r--r-- | tests/stmt/tgenericsunderscore.nim | 4 | ||||
-rw-r--r-- | tests/stmt/tmiscunderscore.nim | 15 | ||||
-rw-r--r-- | tests/template/tunderscore1.nim | 2 |
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 |